Reformat markdown
This commit is contained in:
parent
5a313c952f
commit
b0e7c1aa91
14 changed files with 459 additions and 188 deletions
|
@ -5,6 +5,7 @@ import Structures.Dependent.FreshList
|
|||
|
||||
import Runner
|
||||
```
|
||||
|
||||
<!-- idris
|
||||
import Years.Y2015.Day1
|
||||
import Years.Y2015.Day2
|
||||
|
@ -68,4 +69,3 @@ y2015 = MkYear 2015 [
|
|||
```idris
|
||||
]
|
||||
```
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# Year 2015 Day 1
|
||||
|
||||
Pretty simple, basic warmup problem, nothing really novel is on display here except the effectful part computations.
|
||||
Pretty simple, basic warmup problem, nothing really novel is on display here
|
||||
except the effectful part computations.
|
||||
|
||||
<!-- idris
|
||||
module Years.Y2015.Day1
|
||||
|
@ -14,9 +15,11 @@ import Runner
|
|||
|
||||
## Solver Functions
|
||||
|
||||
This one implements the entirety of the logic for part 1, in a simple tail recursive manner, pattern matching on each character in the input string.
|
||||
This one implements the entirety of the logic for part 1, in a simple tail
|
||||
recursive manner, pattern matching on each character in the input string.
|
||||
|
||||
We include a case for a non-paren character for totality's sake, but since we kinda trust the input here we just don't do anything in that branch.
|
||||
We include a case for a non-paren character for totality's sake, but since we
|
||||
kinda trust the input here we just don't do anything in that branch.
|
||||
|
||||
```idris
|
||||
trackFloor : (start : Integer) -> (xs : List Char) -> Integer
|
||||
|
@ -26,7 +29,9 @@ trackFloor start (')' :: xs) = trackFloor (start - 1) xs
|
|||
trackFloor start (x :: xs) = trackFloor start xs
|
||||
```
|
||||
|
||||
This one is slightly more complicated, ultimately very similar to the above, but with two accumulators, one for the position in the input string in addition to the one for the current floor.
|
||||
This one is slightly more complicated, ultimately very similar to the above, but
|
||||
with two accumulators, one for the position in the input string in addition to
|
||||
the one for the current floor.
|
||||
|
||||
```idris
|
||||
findBasement : (position : Nat) -> (currentFloor : Integer) -> (xs : List Char)
|
||||
|
@ -48,7 +53,8 @@ Both this parts are simple application of one of our solver functions
|
|||
|
||||
### Part 1
|
||||
|
||||
Very uneventful, the only thing novel here is pulling the input out of the `ReaderL "input" String`, which will become very boring very quickly.
|
||||
Very uneventful, the only thing novel here is pulling the input out of the
|
||||
`ReaderL "input" String`, which will become very boring very quickly.
|
||||
|
||||
```idris
|
||||
part1 : Eff (PartEff String) (Integer, ())
|
||||
|
@ -60,7 +66,8 @@ part1 = do
|
|||
|
||||
### Part 2
|
||||
|
||||
We have to be careful to start the position accumulator at 1, since the problem specifies that the input string is 1-index.
|
||||
We have to be careful to start the position accumulator at 1, since the problem
|
||||
specifies that the input string is 1-index.
|
||||
|
||||
```idris
|
||||
part2 : () -> Eff (PartEff String) Nat
|
||||
|
|
|
@ -32,7 +32,9 @@ record Box where
|
|||
|
||||
### Box methods
|
||||
|
||||
`.area` provides the surface area of the box, `.slack` provides the surface area of the smallest face, `.ribbon` provides the smallest perimeter of a face, and `.bow` provides the volume of the box.
|
||||
`.area` provides the surface area of the box, `.slack` provides the surface area
|
||||
of the smallest face, `.ribbon` provides the smallest perimeter of a face, and
|
||||
`.bow` provides the volume of the box.
|
||||
|
||||
Names are as described in the problem
|
||||
|
||||
|
@ -53,14 +55,16 @@ Names are as described in the problem
|
|||
(.bow) (MkBox length width height) = length * width * height
|
||||
```
|
||||
|
||||
Provide the total amount of ribbon needed, by adding the ribbon (smallest perimeter) and the bow (volume) values together.
|
||||
Provide the total amount of ribbon needed, by adding the ribbon (smallest
|
||||
perimeter) and the bow (volume) values together.
|
||||
|
||||
```idris
|
||||
totalRibbon : Box -> Integer
|
||||
totalRibbon x = x.ribbon + x.bow
|
||||
```
|
||||
|
||||
Provide the total amount of paper needed by adding the surface area and the slack (smallest side surface area) together.
|
||||
Provide the total amount of paper needed by adding the surface area and the
|
||||
slack (smallest side surface area) together.
|
||||
|
||||
```idris
|
||||
paperNeeded : Box -> Integer
|
||||
|
@ -77,14 +81,17 @@ parseBox : Has (Except String) fs =>
|
|||
parseBox str = do
|
||||
```
|
||||
|
||||
First, we split the string into 3 parts by pattern matching on the result of `split`, using `pure` to lift the computation into the effect, and throwing an error if the pattern match fails.
|
||||
First, we split the string into 3 parts by pattern matching on the result of
|
||||
`split`, using `pure` to lift the computation into the effect, and throwing an
|
||||
error if the pattern match fails.
|
||||
|
||||
```idris
|
||||
l ::: [w, h] <- pure $ split (== 'x') str
|
||||
| xs => throw "Box did not have exactly 3 components: \{show xs}"
|
||||
```
|
||||
|
||||
Then try to parse each of the three integers, throwing an error if parsing fails, then construct and return our box.
|
||||
Then try to parse each of the three integers, throwing an error if parsing
|
||||
fails, then construct and return our box.
|
||||
|
||||
```idris
|
||||
length <- note "Failed parsing length: \{show l}" $ parsePositive l
|
||||
|
@ -97,9 +104,11 @@ Then try to parse each of the three integers, throwing an error if parsing fails
|
|||
|
||||
### Part 1
|
||||
|
||||
Split the input into lines, effectfully traverse our box parser over the resulting list of lines, then sum the amounts of paper needed for each box.
|
||||
Split the input into lines, effectfully traverse our box parser over the
|
||||
resulting list of lines, then sum the amounts of paper needed for each box.
|
||||
|
||||
We return the list of parsed boxes as the context here to avoid needing to parse again in part 2
|
||||
We return the list of parsed boxes as the context here to avoid needing to parse
|
||||
again in part 2
|
||||
|
||||
```idris
|
||||
part1 : Eff (PartEff String) (Integer, List Box)
|
||||
|
@ -112,7 +121,9 @@ part1 = do
|
|||
|
||||
### Part 2
|
||||
|
||||
Much the same as part 1, except with the amount of ribbon needed instead of the amount of paper, and without the parsing, since we received the already parsed list of boxes from part 1 as our context value.
|
||||
Much the same as part 1, except with the amount of ribbon needed instead of the
|
||||
amount of paper, and without the parsing, since we received the already parsed
|
||||
list of boxes from part 1 as our context value.
|
||||
|
||||
```idris
|
||||
part2 : (boxes : List Box) -> Eff (PartEff String) Integer
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# Year 2015 Day 3
|
||||
|
||||
This day provides a gentle introduction to `mutual` blocks and mutually recursive functions.
|
||||
This day provides a gentle introduction to `mutual` blocks and mutually
|
||||
recursive functions.
|
||||
|
||||
<!-- idris
|
||||
module Years.Y2015.Day3
|
||||
|
@ -23,13 +24,16 @@ import Util
|
|||
|
||||
## Parsing and data structures
|
||||
|
||||
We'll do parsing a little more properly this time, turning the input into a list of movement commands
|
||||
We'll do parsing a little more properly this time, turning the input into a list
|
||||
of movement commands
|
||||
|
||||
```idris
|
||||
data Movement = North | East | South | West
|
||||
```
|
||||
|
||||
We need an effectful operation to parse a single char into a movement. We'll pattern match on the char, and include a catch-all case that throws an error in the event of an invalid char
|
||||
We need an effectful operation to parse a single char into a movement. We'll
|
||||
pattern match on the char, and include a catch-all case that throws an error in
|
||||
the event of an invalid char
|
||||
|
||||
```idris
|
||||
parseMovement : Has (Except String) fs => (x : Char) -> Eff fs Movement
|
||||
|
@ -40,7 +44,9 @@ parseMovement '<' = pure West
|
|||
parseMovement x = throw "Invalid Movement: \{show x}"
|
||||
```
|
||||
|
||||
We also need to be able to translate a `Movement` into a vector of length one pointing in the given direction in coordinate space. Somewhat arbitrarily, we chose 'North' to be positive x and 'East' to be positive y.
|
||||
We also need to be able to translate a `Movement` into a vector of length one
|
||||
pointing in the given direction in coordinate space. Somewhat arbitrarily, we
|
||||
chose 'North' to be positive x and 'East' to be positive y.
|
||||
|
||||
```idris
|
||||
vector : Movement -> (Integer, Integer)
|
||||
|
@ -54,11 +60,17 @@ vector West = (0, -1)
|
|||
|
||||
### Visited houses
|
||||
|
||||
This is a pretty simple task, we are just applying the movements to our current position, and adding our current position to the set of visited locations, so we'll handle this with a normal tail recursive function.
|
||||
This is a pretty simple task, we are just applying the movements to our current
|
||||
position, and adding our current position to the set of visited locations, so
|
||||
we'll handle this with a normal tail recursive function.
|
||||
|
||||
To keep the api nice, we wont ask for the set or the starting location in the top-level function, and instead have the top level function initialize the set and location before passing control to the inner tail-recursive variant.
|
||||
To keep the api nice, we wont ask for the set or the starting location in the
|
||||
top-level function, and instead have the top level function initialize the set
|
||||
and location before passing control to the inner tail-recursive variant.
|
||||
|
||||
Because the starting location gets a present, we'll add our location to the set before performing the movement, so we will need to add our final location to the set in the recursive base case.
|
||||
Because the starting location gets a present, we'll add our location to the set
|
||||
before performing the movement, so we will need to add our final location to the
|
||||
set in the recursive base case.
|
||||
|
||||
```idris
|
||||
visitedLocations : List Movement -> SortedSet (Integer, Integer)
|
||||
|
@ -73,13 +85,26 @@ visitedLocations xs = visitor xs empty (0, 0)
|
|||
|
||||
### Robo Santa
|
||||
|
||||
This one gets a bit more interesting, we'll adopt the same tail recursive approach, but instead use a `mutual` block and two mutually recursive functions to handle the alternation between santa and robo santa. The `visitSanta` function will pass control to `visitRobo` after executing its movement, and vise versa.
|
||||
This one gets a bit more interesting, we'll adopt the same tail recursive
|
||||
approach, but instead use a `mutual` block and two mutually recursive functions
|
||||
to handle the alternation between santa and robo santa. The `visitSanta`
|
||||
function will pass control to `visitRobo` after executing its movement, and vise
|
||||
versa.
|
||||
|
||||
We'll want to insert both present deliverer's locations in the recursive base case, this may result in a duplicate location, but that's okay because `SortedSet` will only hold at most one of each item inserted into it.
|
||||
We'll want to insert both present deliverer's locations in the recursive base
|
||||
case, this may result in a duplicate location, but that's okay because
|
||||
`SortedSet` will only hold at most one of each item inserted into it.
|
||||
|
||||
In idris, there is a general requirement that values be defined before their use, a common feature of dependently typed languages, resulting from the fact that just having the type signature of a function/value alone is not always enough to perform type checking, as functions can appear as part of types, requiring evaluation of the function and making automatic dependency analysis effectively impossible.
|
||||
In idris, there is a general requirement that values be defined before their
|
||||
use, a common feature of dependently typed languages, resulting from the fact
|
||||
that just having the type signature of a function/value alone is not always
|
||||
enough to perform type checking, as functions can appear as part of types,
|
||||
requiring evaluation of the function and making automatic dependency analysis
|
||||
effectively impossible.
|
||||
|
||||
Inside a `mutual` block, elaboration behaves differently, elaborating types first and then values in separate passes. This restricts what you can do a little, but enables mutually recursive functions.
|
||||
Inside a `mutual` block, elaboration behaves differently, elaborating types
|
||||
first and then values in separate passes. This restricts what you can do a
|
||||
little, but enables mutually recursive functions.
|
||||
|
||||
```idris
|
||||
visitedLocations' : List Movement -> SortedSet (Integer, Integer)
|
||||
|
@ -103,7 +128,9 @@ visitedLocations' xs = visitSanta xs empty (0, 0) (0, 0)
|
|||
|
||||
### Part 1
|
||||
|
||||
Similar to the previous day, we get our input, unpack it, and traverse our effectful movement parsing function over it, before feeding that into our solving function.
|
||||
Similar to the previous day, we get our input, unpack it, and traverse our
|
||||
effectful movement parsing function over it, before feeding that into our
|
||||
solving function.
|
||||
|
||||
```idris
|
||||
part1 : Eff (PartEff String) (Nat, List Movement)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# Year 2015 Day 4
|
||||
|
||||
This day introduces us to a little bit of FFI, linking to openssl to use it's `MD5` functionality.
|
||||
This day introduces us to a little bit of FFI, linking to openssl to use it's
|
||||
`MD5` functionality.
|
||||
|
||||
<!-- idris
|
||||
module Years.Y2015.Day4
|
||||
|
@ -19,15 +20,22 @@ import Prim.Array
|
|||
|
||||
## FFI
|
||||
|
||||
We will be using openssl's `MD5` function to do the hashing, so we will need to provide a couple of foreign function definitions.
|
||||
We will be using openssl's `MD5` function to do the hashing, so we will need to
|
||||
provide a couple of foreign function definitions.
|
||||
|
||||
First, we'll provide one for the C standard library's `strlen`. We could get this value from within idris, but we'll do it through FFI to flesh out the example a bit more.
|
||||
First, we'll provide one for the C standard library's `strlen`. We could get
|
||||
this value from within idris, but we'll do it through FFI to flesh out the
|
||||
example a bit more.
|
||||
|
||||
The format of the specifier after `%foreign` is "language:function name,library name". Since `strlen` is the c standard library, we use `libc` as the library name.
|
||||
The format of the specifier after `%foreign` is "language:function name,library
|
||||
name". Since `strlen` is the c standard library, we use `libc` as the library
|
||||
name.
|
||||
|
||||
Idris will automatically convert primitive types like `String`s and `Int`s to their C equivalents at the FFI boundary.
|
||||
Idris will automatically convert primitive types like `String`s and `Int`s to
|
||||
their C equivalents at the FFI boundary.
|
||||
|
||||
`PrimIO` is a special, basic type of IO action based on linear types, we won't be getting into the specifics right now.
|
||||
`PrimIO` is a special, basic type of IO action based on linear types, we won't
|
||||
be getting into the specifics right now.
|
||||
|
||||
By convention, primitive FFI function names start with `prim__` in idris.
|
||||
|
||||
|
@ -36,9 +44,12 @@ By convention, primitive FFI function names start with `prim__` in idris.
|
|||
prim__strlen : String -> PrimIO Int
|
||||
```
|
||||
|
||||
Now we need one for the `MD5` function from openssl, the openssl objectfile calls itself `libcrypto`, so we'll use that as the library name.
|
||||
Now we need one for the `MD5` function from openssl, the openssl objectfile
|
||||
calls itself `libcrypto`, so we'll use that as the library name.
|
||||
|
||||
The `MD5` function actually returns a pointer to the hash value, but since the 3rd argument is the pointer it expects to write the hash to, we already have that value and we'll ignore it by having our `prim__md5` return unit.
|
||||
The `MD5` function actually returns a pointer to the hash value, but since the
|
||||
3rd argument is the pointer it expects to write the hash to, we already have
|
||||
that value and we'll ignore it by having our `prim__md5` return unit.
|
||||
|
||||
The second argument is the length of the input string in bytes.
|
||||
|
||||
|
@ -47,15 +58,25 @@ The second argument is the length of the input string in bytes.
|
|||
prim__md5 : String -> Int -> Ptr Bits8 -> PrimIO ()
|
||||
```
|
||||
|
||||
Now we glue these parts together in our higher level `md5'` function.
|
||||
Now we glue these parts together in our higher level `md5'` function.
|
||||
|
||||
We use `malloc` from `System.FFI` to allocate a buffer for `prim__md5` to write to. This returns an `AnyPtr`, when we need a `Ptr Bits8`, so we'll need to cast it, using `prim__castPtr` from the prelude.
|
||||
We use `malloc` from `System.FFI` to allocate a buffer for `prim__md5` to write
|
||||
to. This returns an `AnyPtr`, when we need a `Ptr Bits8`, so we'll need to cast
|
||||
it, using `prim__castPtr` from the prelude.
|
||||
|
||||
We then use our `prim__strlen` function to get the length of our input string from within C, using `primIO` from the prelude to "lift" the resulting `PrimIO Int` into our `io` context.
|
||||
We then use our `prim__strlen` function to get the length of our input string
|
||||
from within C, using `primIO` from the prelude to "lift" the resulting
|
||||
`PrimIO Int` into our `io` context.
|
||||
|
||||
We then feed the string, the calculated length, and our casted buffer into our `prim__md5` function, then use `prim__getArrayBits8` from the [c-ffi](https://github.com/joelberkeley/c-ffi/) library to extract each of the bytes, passing them into a pure idris helper function to convert into hex representation.
|
||||
We then feed the string, the calculated length, and our casted buffer into our
|
||||
`prim__md5` function, then use `prim__getArrayBits8` from the
|
||||
[c-ffi](https://github.com/joelberkeley/c-ffi/) library to extract each of the
|
||||
bytes, passing them into a pure idris helper function to convert into hex
|
||||
representation.
|
||||
|
||||
Since we are working with C FFI and performing a little bit of manual memory management here, we must remember to `free` our pointer, and then we can splice together our output string and return.
|
||||
Since we are working with C FFI and performing a little bit of manual memory
|
||||
management here, we must remember to `free` our pointer, and then we can splice
|
||||
together our output string and return.
|
||||
|
||||
```idris
|
||||
md5' : HasIO io => String -> io String
|
||||
|
@ -77,21 +98,38 @@ md5' str = do
|
|||
pack [hexDigit (n `shiftR` 4), hexDigit (n .&. 0xF)]
|
||||
```
|
||||
|
||||
Now, because foreign functions return a `PrimIO` by default, our `md'` still requires some sort of `HasIO` (like IO). This is undesirable here, after all, MD5 is a hash function, so it really _ought_ to behave like a pure function. We know from taking a careful look at what FFI functions we are invoking that we aren't altering any global state.
|
||||
Now, because foreign functions return a `PrimIO` by default, our `md'` still
|
||||
requires some sort of `HasIO` (like IO). This is undesirable here, after all,
|
||||
MD5 is a hash function, so it really _ought_ to behave like a pure function. We
|
||||
know from taking a careful look at what FFI functions we are invoking that we
|
||||
aren't altering any global state.
|
||||
|
||||
Idris has an escape hatch for these types of situations, `unsafePerformIO`. This function is, as the name suggests, _quite_ unsafe, so it shouldn't be used if you don't fully understand the implications, and only sparingly at that, but doing FFI to C is an intrinsically unsafe operation, so it's morally okay to use here, as long as we are careful to not violate referential transparency or type safety. Haskell programmers should note that `unsafePerformIO` is _much_ easier to use correctly in Idris, largely as a result of Idris being strict by default.
|
||||
Idris has an escape hatch for these types of situations, `unsafePerformIO`. This
|
||||
function is, as the name suggests, _quite_ unsafe, so it shouldn't be used if
|
||||
you don't fully understand the implications, and only sparingly at that, but
|
||||
doing FFI to C is an intrinsically unsafe operation, so it's morally okay to use
|
||||
here, as long as we are careful to not violate referential transparency or type
|
||||
safety. Haskell programmers should note that `unsafePerformIO` is _much_ easier
|
||||
to use correctly in Idris, largely as a result of Idris being strict by default.
|
||||
|
||||
Finally, we'll construct our top level `md5` function, wrapping `md5'` in `unsafePerformIO`
|
||||
Finally, we'll construct our top level `md5` function, wrapping `md5'` in
|
||||
`unsafePerformIO`
|
||||
|
||||
```idris
|
||||
md5 : String -> String
|
||||
md5 str = unsafePerformIO $ md5' str
|
||||
```
|
||||
|
||||
## Solver functions
|
||||
|
||||
### Count the leading zeros in a string
|
||||
|
||||
There is a more clever way to do this making use of a "view" strings have for interacting with them as if they were lazy lists, but we've already covered so much here that we'll save that one for another time and just do it the straight forward way, having a top level function that accepts a string, turns it into a list of chars, and then pass it into a tail recursive helper function to actually count the zeros.
|
||||
There is a more clever way to do this making use of a "view" strings have for
|
||||
interacting with them as if they were lazy lists, but we've already covered so
|
||||
much here that we'll save that one for another time and just do it the straight
|
||||
forward way, having a top level function that accepts a string, turns it into a
|
||||
list of chars, and then pass it into a tail recursive helper function to
|
||||
actually count the zeros.
|
||||
|
||||
```idris
|
||||
countZeros : String -> Nat
|
||||
|
@ -116,7 +154,10 @@ checkZeros key number =
|
|||
|
||||
### Find the first input with a specific number of zeros
|
||||
|
||||
Search numbers starting at the provided seed and counting up in a tail recursive helper function. This function is going to be effectively impossible to prove totality for, so we have removed our normal `%default total` annotation for this file
|
||||
Search numbers starting at the provided seed and counting up in a tail recursive
|
||||
helper function. This function is going to be effectively impossible to prove
|
||||
totality for, so we have removed our normal `%default total` annotation for this
|
||||
file
|
||||
|
||||
```idris
|
||||
findFirst : (zeros : Nat) -> (key : String) -> (seed : Nat) -> Nat
|
||||
|
@ -130,7 +171,10 @@ findFirst zeros key current =
|
|||
|
||||
### Part 1
|
||||
|
||||
Pass the input into our `findFirst` function and use the result as both our output and context value. Since part2 is searching for a longer run of 0s than part 1, numbers already checked by part 1 can not possibly be valid solutions for part 2, so we can skip them in part 2.
|
||||
Pass the input into our `findFirst` function and use the result as both our
|
||||
output and context value. Since part2 is searching for a longer run of 0s than
|
||||
part 1, numbers already checked by part 1 can not possibly be valid solutions
|
||||
for part 2, so we can skip them in part 2.
|
||||
|
||||
```idris
|
||||
part1 : Eff (PartEff String) (Nat, Nat)
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
# Year 2015 Day 5
|
||||
|
||||
This day provides a nice chance to introduce [views](https://idris2.readthedocs.io/en/latest/tutorial/views.html), specifically `String`'s [`AsList`](https://www.idris-lang.org/docs/idris2/current/base_docs/docs/Data.String.html#Data.String.AsList) view, which lets us treat `String`s as if they were lazy lists or iterators.
|
||||
This day provides a nice chance to introduce
|
||||
[views](https://idris2.readthedocs.io/en/latest/tutorial/views.html),
|
||||
specifically `String`'s
|
||||
[`AsList`](https://www.idris-lang.org/docs/idris2/current/base_docs/docs/Data.String.html#Data.String.AsList)
|
||||
view, which lets us treat `String`s as if they were lazy lists or iterators.
|
||||
|
||||
<!-- idris
|
||||
module Years.Y2015.Day5
|
||||
|
@ -31,19 +35,39 @@ vowels : List Char
|
|||
vowels = ['a', 'e', 'i', 'o', 'u']
|
||||
```
|
||||
|
||||
Now define a function to check if a string contains a minimum number of vowels. Here we make use of the `with` rule and the `AsList` view over strings.
|
||||
Now define a function to check if a string contains a minimum number of vowels.
|
||||
Here we make use of the `with` rule and the `AsList` view over strings.
|
||||
|
||||
The `with` rule provides dependently typed pattern matching, allowing us to make use of the fact that the form of some of the arguments can be determined by the values of the others.
|
||||
The `with` rule provides dependently typed pattern matching, allowing us to make
|
||||
use of the fact that the form of some of the arguments can be determined by the
|
||||
values of the others.
|
||||
|
||||
The `AsList` view associates a `String` with a lazy list of the characters in the string by providing incremental proofs that the `String` decomposes into the lazy list. An `AsList` of value `Nil`/`[]` proves that the associated string is empty, and an `AsList` of value `c :: xs` proves that the associated string can be generated by `strCons`ing the character `c` onto the rest of the string as captured by the `AsList` in `xs`.
|
||||
The `AsList` view associates a `String` with a lazy list of the characters in
|
||||
the string by providing incremental proofs that the `String` decomposes into the
|
||||
lazy list. An `AsList` of value `Nil`/`[]` proves that the associated string is
|
||||
empty, and an `AsList` of value `c :: xs` proves that the associated string can
|
||||
be generated by `strCons`ing the character `c` onto the rest of the string as
|
||||
captured by the `AsList` in `xs`.
|
||||
|
||||
We use the `asList` function, which generates the `AsList` view for the provided string, as the argument to the `with` block. Functions like this that produce views for a value are refereed to as "covering functions". In general, views are dependently typed constructs whose value provides insight into the structure of another value.
|
||||
We use the `asList` function, which generates the `AsList` view for the provided
|
||||
string, as the argument to the `with` block. Functions like this that produce
|
||||
views for a value are refereed to as "covering functions". In general, views are
|
||||
dependently typed constructs whose value provides insight into the structure of
|
||||
another value.
|
||||
|
||||
We implement this function by keeping a counter of the number of vowels we still need to see to accept the string, returning true when that counter equals zero, or false if we hit the end of the string (when the string argument is empty and the associated `AsList` is `Nil`) while the counter is still non-zero.
|
||||
We implement this function by keeping a counter of the number of vowels we still
|
||||
need to see to accept the string, returning true when that counter equals zero,
|
||||
or false if we hit the end of the string (when the string argument is empty and
|
||||
the associated `AsList` is `Nil`) while the counter is still non-zero.
|
||||
|
||||
If the counter is non-zero and the string is non-empty, we check the current `c` character to see if it is a vowel, by checking to see if our `vowels` list contains it, then recurse, decrementing the counter if it was a vowel, and leaving the counter unchanged if it wasn't.
|
||||
If the counter is non-zero and the string is non-empty, we check the current `c`
|
||||
character to see if it is a vowel, by checking to see if our `vowels` list
|
||||
contains it, then recurse, decrementing the counter if it was a vowel, and
|
||||
leaving the counter unchanged if it wasn't.
|
||||
|
||||
The use of `| xs` after the recursive call to `containsVowels` tells Idris to recurse directly to the `with` block, allowing it to reuse the `AsList` value in `xs` without having to generate the `AsList` view again.
|
||||
The use of `| xs` after the recursive call to `containsVowels` tells Idris to
|
||||
recurse directly to the `with` block, allowing it to reuse the `AsList` value in
|
||||
`xs` without having to generate the `AsList` view again.
|
||||
|
||||
```idris
|
||||
containsVowels : (count : Nat) -> String -> Bool
|
||||
|
@ -58,11 +82,18 @@ containsVowels count str with (asList str)
|
|||
|
||||
### At least one letter appears twice in a row
|
||||
|
||||
For this one, since we need to inspect two letters at a time, we break out a nested `with` block, with the tail of the `AsList` as the argument of the block, to also pattern match on the next character of the string.
|
||||
For this one, since we need to inspect two letters at a time, we break out a
|
||||
nested `with` block, with the tail of the `AsList` as the argument of the block,
|
||||
to also pattern match on the next character of the string.
|
||||
|
||||
Since the `AsList` view is lazy, we need to use `force` in the inner with block to force the evaluation of the next component of the view, this is due to a [bug in the compiler](https://github.com/idris-lang/Idris2/issues/3461).
|
||||
Since the `AsList` view is lazy, we need to use `force` in the inner with block
|
||||
to force the evaluation of the next component of the view, this is due to a
|
||||
[bug in the compiler](https://github.com/idris-lang/Idris2/issues/3461).
|
||||
|
||||
The logic here is pretty simple, if we hit the empty string or get down to one character, return `False`, if we have two characters available, compare them, returning `True` if they match, and recursing to the outer with block, only consuming one of the two characters, if they don't match.
|
||||
The logic here is pretty simple, if we hit the empty string or get down to one
|
||||
character, return `False`, if we have two characters available, compare them,
|
||||
returning `True` if they match, and recursing to the outer with block, only
|
||||
consuming one of the two characters, if they don't match.
|
||||
|
||||
```idris
|
||||
doubleLetter : String -> Bool
|
||||
|
@ -85,7 +116,10 @@ disallowedSubstrs : List String
|
|||
disallowedSubstrs = ["ab", "cd", "pq", "xy"]
|
||||
```
|
||||
|
||||
Idris's standard library does not currently define a function to check if a string is a substring of another, so we instead use our own `isSubstr` function, defined via use of the `AsList` view, from our [Util](../../Util.md#issubstr) module.
|
||||
Idris's standard library does not currently define a function to check if a
|
||||
string is a substring of another, so we instead use our own `isSubstr` function,
|
||||
defined via use of the `AsList` view, from our [Util](../../Util.md#issubstr)
|
||||
module.
|
||||
|
||||
```idris
|
||||
noDisallowed : String -> Bool
|
||||
|
@ -95,7 +129,8 @@ noDisallowed str =
|
|||
|
||||
### isNice
|
||||
|
||||
Combine all three of our above functions together to check if a string is a "nice" string
|
||||
Combine all three of our above functions together to check if a string is a
|
||||
"nice" string
|
||||
|
||||
```idris
|
||||
isNice : String -> Bool
|
||||
|
@ -106,7 +141,9 @@ isNice str = containsVowels 3 str && doubleLetter str && noDisallowed str
|
|||
|
||||
### Pair of letters that appears twice
|
||||
|
||||
Use a similar approach to `doubleLetter` above, but instead of comparing the two chars for equality, pack them into a string and check if they are a substring of the rest of the string.
|
||||
Use a similar approach to `doubleLetter` above, but instead of comparing the two
|
||||
chars for equality, pack them into a string and check if they are a substring of
|
||||
the rest of the string.
|
||||
|
||||
```idris
|
||||
pairTwice : String -> Bool
|
||||
|
@ -122,7 +159,9 @@ pairTwice str with (asList str)
|
|||
|
||||
### Letter that repeats after one letter
|
||||
|
||||
More of the same, but this one gets extra fun because we are matching 3 letters at a time. This would be a lot nicer if we could get away without nesting the `with` blocks, but due to the aformentioned compiler bug, we need the nesting.
|
||||
More of the same, but this one gets extra fun because we are matching 3 letters
|
||||
at a time. This would be a lot nicer if we could get away without nesting the
|
||||
`with` blocks, but due to the aformentioned compiler bug, we need the nesting.
|
||||
|
||||
```idris
|
||||
letterRepeatGap : String -> Bool
|
||||
|
@ -151,7 +190,8 @@ isNice' str = pairTwice str && letterRepeatGap str
|
|||
|
||||
### Part 1
|
||||
|
||||
Split our input up into a list of its component strings, and then count how many satisfy the "nice" criteria.
|
||||
Split our input up into a list of its component strings, and then count how many
|
||||
satisfy the "nice" criteria.
|
||||
|
||||
```idris
|
||||
part1 : Eff (PartEff String) (Nat, ())
|
||||
|
|
|
@ -27,7 +27,9 @@ import Data.IORef
|
|||
|
||||
### Grid structure
|
||||
|
||||
Since this is our first 2d grid problem, we'll keep it simple and use a `Vect` of `Vect`s to store our problem state, we'll revisit a more complicated but more efficient structure for storing a 2d `Grid` in a later problem.
|
||||
Since this is our first 2d grid problem, we'll keep it simple and use a `Vect`
|
||||
of `Vect`s to store our problem state, we'll revisit a more complicated but more
|
||||
efficient structure for storing a 2d `Grid` in a later problem.
|
||||
|
||||
This alias adds 1 to each of its arguments to ensure non-emptyness.
|
||||
|
||||
|
@ -36,7 +38,8 @@ Grid : (rows, cols : Nat) -> Type -> Type
|
|||
Grid rows cols e = Vect (S rows) (Vect (S cols) e)
|
||||
```
|
||||
|
||||
We also provide a type alias for the coordinates in this grid, a simple pair of `Fin`s.
|
||||
We also provide a type alias for the coordinates in this grid, a simple pair of
|
||||
`Fin`s.
|
||||
|
||||
```idris
|
||||
Coord : (rows, cols : Nat) -> Type
|
||||
|
@ -93,7 +96,9 @@ Show (Range rows cols) where
|
|||
|
||||
Helper function to extract a range of values from our Grid.
|
||||
|
||||
First extracts the range of rows this `Range` touches, then maps a an extractor for the range of columns it touches across them, before lazily concatenating the resulting lists.
|
||||
First extracts the range of rows this `Range` touches, then maps a an extractor
|
||||
for the range of columns it touches across them, before lazily concatenating the
|
||||
resulting lists.
|
||||
|
||||
```idris
|
||||
extractRange : Range rows cols -> Grid rows cols e -> LazyList e
|
||||
|
@ -118,7 +123,7 @@ Show (Command rows cols) where
|
|||
"{\{show action}: \{show range}}"
|
||||
-->
|
||||
|
||||
### Parsing
|
||||
### Parsing
|
||||
|
||||
Pattern match on the action string, throwing an error if the action was invalid
|
||||
|
||||
|
@ -131,7 +136,8 @@ parseAction "toggle" = pure Toggle
|
|||
parseAction str = throw "Invalid action: \{str}"
|
||||
```
|
||||
|
||||
Attempt to split the string into two parts on the comma, and then attempt to parse the parts as `Fin`s, throwing an appropriate error if anything goes wrong
|
||||
Attempt to split the string into two parts on the comma, and then attempt to
|
||||
parse the parts as `Fin`s, throwing an appropriate error if anything goes wrong
|
||||
|
||||
```idris
|
||||
parseCoord : Has (Except String) fs =>
|
||||
|
@ -157,7 +163,8 @@ parseRange pair1 pair2 = do
|
|||
pure $ MkRange top_left bottom_right
|
||||
```
|
||||
|
||||
Split a string into a list of parts, pattern matching those parts to attempt to extract a `Command`.
|
||||
Split a string into a list of parts, pattern matching those parts to attempt to
|
||||
extract a `Command`.
|
||||
|
||||
```idris
|
||||
parseCommand : Has (Except String) fs => Has Logger fs =>
|
||||
|
@ -181,7 +188,10 @@ parseCommand input = do
|
|||
|
||||
## Problem structure
|
||||
|
||||
Since we are dealing with a million slots here, we'll want some level of true mutability. The actual structure containing the slots doesn't need to be modified once its setup, so we'll make the mutability interior to the slots and keep a `Grid` of `IORef`s.
|
||||
Since we are dealing with a million slots here, we'll want some level of true
|
||||
mutability. The actual structure containing the slots doesn't need to be
|
||||
modified once its setup, so we'll make the mutability interior to the slots and
|
||||
keep a `Grid` of `IORef`s.
|
||||
|
||||
We'll setup a helper function to initialize our grid based on a seed value.
|
||||
|
||||
|
@ -193,7 +203,8 @@ ioGrid rows cols seed =
|
|||
in traverse (traverse id) grid
|
||||
```
|
||||
|
||||
Convert a `Grid` of `IORef`s into a `Grid` of pure values by traversing the `readIORef` operation over our `Grid`.
|
||||
Convert a `Grid` of `IORef`s into a `Grid` of pure values by traversing the
|
||||
`readIORef` operation over our `Grid`.
|
||||
|
||||
```idris
|
||||
purify : Has IO fs =>
|
||||
|
@ -208,9 +219,12 @@ purify grid = traverse (traverse readIORef) grid
|
|||
```idris
|
||||
namespace Part1
|
||||
```
|
||||
Apply a given command to our `Grid` of `IORef`s.
|
||||
|
||||
Use our `extractRange` function to extract all the `IORef`s in the grid cells touched by our `Range` and then traverse an appropriate mutating action over them.
|
||||
Apply a given command to our `Grid` of `IORef`s.
|
||||
|
||||
Use our `extractRange` function to extract all the `IORef`s in the grid cells
|
||||
touched by our `Range` and then traverse an appropriate mutating action over
|
||||
them.
|
||||
|
||||
```idris
|
||||
applyCommand : Has IO fs =>
|
||||
|
@ -223,7 +237,8 @@ Use our `extractRange` function to extract all the `IORef`s in the grid cells to
|
|||
Toggle => Lazy.traverse_ (`modifyIORef` not) cells
|
||||
```
|
||||
|
||||
Apply a list of commands to our `Grid` of `IORef`s, doing some debug logging along the way.
|
||||
Apply a list of commands to our `Grid` of `IORef`s, doing some debug logging
|
||||
along the way.
|
||||
|
||||
```idris
|
||||
export
|
||||
|
@ -246,7 +261,8 @@ Apply a list of commands to our `Grid` of `IORef`s, doing some debug logging alo
|
|||
namespace Part2
|
||||
```
|
||||
|
||||
Much the same as above, but instead we apply the part 2 rules to a `Grid` of `Nat`.
|
||||
Much the same as above, but instead we apply the part 2 rules to a `Grid` of
|
||||
`Nat`.
|
||||
|
||||
```idris
|
||||
applyCommand : Has IO fs =>
|
||||
|
@ -259,7 +275,9 @@ Much the same as above, but instead we apply the part 2 rules to a `Grid` of `Na
|
|||
Toggle => Lazy.traverse_ (`modifyIORef` (+ 2)) cells
|
||||
```
|
||||
|
||||
Identical to above, except for using our part 2 `applyCommand`. We can use the same name here because we have the two variants behind namespaces and Idris can disambiguate via the types.
|
||||
Identical to above, except for using our part 2 `applyCommand`. We can use the
|
||||
same name here because we have the two variants behind namespaces and Idris can
|
||||
disambiguate via the types.
|
||||
|
||||
```idris
|
||||
export
|
||||
|
@ -280,7 +298,9 @@ Identical to above, except for using our part 2 `applyCommand`. We can use the s
|
|||
|
||||
### Part 1
|
||||
|
||||
Parse our commands, generate our initial `Grid` with all the lights turned off (represented by `False`), apply our commands to it, purify it, and count the lights that are turned on.
|
||||
Parse our commands, generate our initial `Grid` with all the lights turned off
|
||||
(represented by `False`), apply our commands to it, purify it, and count the
|
||||
lights that are turned on.
|
||||
|
||||
Pass out our list of parsed commands as the context for reuse in part 2.
|
||||
|
||||
|
@ -298,7 +318,9 @@ part1 = do
|
|||
|
||||
### Part 2
|
||||
|
||||
This time, use an initial `Grid` with all brightness values at 0, apply our list of preparsed commands using our part 2 `applyCommands` function (selected via the type signature), and then add up the brightnesses.
|
||||
This time, use an initial `Grid` with all brightness values at 0, apply our list
|
||||
of preparsed commands using our part 2 `applyCommands` function (selected via
|
||||
the type signature), and then add up the brightnesses.
|
||||
|
||||
```idris
|
||||
part2 : List (Command 999 999) -> Eff (PartEff String) Nat
|
||||
|
|
|
@ -2,9 +2,16 @@
|
|||
|
||||
This day provides us a gentle introduction to dependent maps.
|
||||
|
||||
Per the problem specification, each wire can only be output to by one gate. To encode this property in the type, we'll tag `Gate` with the output wire in the type, and then store our ciruct as a dependent map from wires to `Gate`s. This ensures that the circut only contains one gate outputting to each wire, and that looking up a wire in the ciruct produces exactly the gate that outputs to it through type checking.
|
||||
Per the problem specification, each wire can only be output to by one gate. To
|
||||
encode this property in the type, we'll tag `Gate` with the output wire in the
|
||||
type, and then store our ciruct as a dependent map from wires to `Gate`s. This
|
||||
ensures that the circut only contains one gate outputting to each wire, and that
|
||||
looking up a wire in the ciruct produces exactly the gate that outputs to it
|
||||
through type checking.
|
||||
|
||||
Ensuring that the input contains only one gate outputting for each wire is done through throwing a runtime error in the parsing function if a second gate outputting to a given wire is found in the input.
|
||||
Ensuring that the input contains only one gate outputting for each wire is done
|
||||
through throwing a runtime error in the parsing function if a second gate
|
||||
outputting to a given wire is found in the input.
|
||||
|
||||
<!-- idris
|
||||
module Years.Y2015.Day7
|
||||
|
@ -32,7 +39,8 @@ import Decidable.Equality
|
|||
|
||||
### Problem structure
|
||||
|
||||
Define type aliases for wires and literals, and specify that an input can be either a literal or a wire.
|
||||
Define type aliases for wires and literals, and specify that an input can be
|
||||
either a literal or a wire.
|
||||
|
||||
```idris
|
||||
Wire : Type
|
||||
|
@ -128,9 +136,16 @@ parseGate str = do
|
|||
_ => throw "Invalid Gate: \{str}"
|
||||
```
|
||||
|
||||
Break the input into lines, traverse `parseGate` over the lines, and collect the results into our circut DMap.
|
||||
Break the input into lines, traverse `parseGate` over the lines, and collect the
|
||||
results into our circut DMap.
|
||||
|
||||
The type of a value in a `SortedDMap` depends on the value of the key that refers to it, in the type constructor `SortedDMap k v`, `v` is of type `k -> Type`, this is the function the map calls to calculate the type of the value from the value of the key. This, not so coincidentally, is the type signature of our `Gate` type constructor, which we can slot in here to get a map from a wire to the gate outputting to that wire, preventing us from accidentally associating a wire to the wrong gate.
|
||||
The type of a value in a `SortedDMap` depends on the value of the key that
|
||||
refers to it, in the type constructor `SortedDMap k v`, `v` is of type
|
||||
`k -> Type`, this is the function the map calls to calculate the type of the
|
||||
value from the value of the key. This, not so coincidentally, is the type
|
||||
signature of our `Gate` type constructor, which we can slot in here to get a map
|
||||
from a wire to the gate outputting to that wire, preventing us from accidentally
|
||||
associating a wire to the wrong gate.
|
||||
|
||||
```idris
|
||||
parseGates : Has (Except String) fs => Has Logger fs =>
|
||||
|
@ -150,7 +165,8 @@ parseGates input = do
|
|||
|
||||
## Solver Functions
|
||||
|
||||
Recursively solve for the value on a wire, keeping a cache of already solved wires
|
||||
Recursively solve for the value on a wire, keeping a cache of already solved
|
||||
wires
|
||||
|
||||
```idris
|
||||
covering
|
||||
|
@ -197,7 +213,10 @@ solveWire wire = do
|
|||
|
||||
### Part 1
|
||||
|
||||
Parse the input, then feed it and an initial empty cache into our `solveWire` function, passing the produced value as the output and the circut, represented as a dependent map from wires to gates, as well as the output signal from wire 'a' as the context for part 2.
|
||||
Parse the input, then feed it and an initial empty cache into our `solveWire`
|
||||
function, passing the produced value as the output and the circut, represented
|
||||
as a dependent map from wires to gates, as well as the output signal from wire
|
||||
'a' as the context for part 2.
|
||||
|
||||
```idris
|
||||
covering
|
||||
|
@ -214,7 +233,8 @@ part1 = do
|
|||
|
||||
### Part 2
|
||||
|
||||
Override the value for the 'b' wire to the output from the 'a' wire in part 1, then rerun our calcuation to find the new output for the 'a' wire.
|
||||
Override the value for the 'b' wire to the output from the 'a' wire in part 1,
|
||||
then rerun our calcuation to find the new output for the 'a' wire.
|
||||
|
||||
```idris
|
||||
covering
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue