Hello there.
I’m new to this discourse and really Purescript. I took a few tries at learning FP, first with Haskell, then with Purescript and I think I’m finally making headway in a substantial way.
To put the skills into practice I took adventofcode as a playground. Not long into the puzzles and I can’t seem to solve a problem due to recursion and I’m not there yet with different monads to be able to solve it with State/ST/Ref yet so I’d really appreciate some pointers.
The problem is:
— Day 7: Some Assembly Required —
This year, Santa brought little Bobby Tables a set of wires and bitwise logic gates! Unfortunately, little Bobby is a little under the recommended age range, and he needs help assembling the circuit.
Each wire has an identifier (some lowercase letters) and can carry a 16-bit signal (a number from 0
to 65535
). A signal is provided to each wire by a gate, another wire, or some specific value. Each wire can only get a signal from one source, but can provide its signal to multiple destinations. A gate provides no signal until all of its inputs have a signal.
The included instructions booklet describes how to connect the parts together: x AND y -> z
means to connect wires x
and y
to an AND gate, and then connect its output to wire z
.
For example:
-
123 -> x
means that the signal123
is provided to wirex
. -
x AND y -> z
means that the bitwise AND of wirex
and wirey
is provided to wirez
. -
p LSHIFT 2 -> q
means that the value from wirep
is left-shifted by2
and then provided to wireq
. -
NOT e -> f
means that the bitwise complement of the value from wiree
is provided to wiref
.
Other possible gates include OR
(bitwise OR) and RSHIFT
(right-shift). If, for some reason, you’d like to emulate the circuit instead, almost all programming languages (for example, C, JavaScript, or Python) provide operators for these gates.
For example, here is a simple circuit:
123 -> x
456 -> y
x AND y -> d
x OR y -> e
x LSHIFT 2 -> f
y RSHIFT 2 -> g
NOT x -> h
NOT y -> i
After it is run, these are the signals on the wires:
d: 72
e: 507
f: 492
g: 114
h: 65412
i: 65079
x: 123
y: 456
In little Bobby’s kit’s instructions booklet (provided as your puzzle input), what signal is ultimately provided to wire a
?
And my solution is https://github.com/razcore-art/adventofcode/blob/master/src/AOC2015/D07.purs. Which I’m also pasting here for easier access:
module AOC2015.D07 where
import Prelude
import Data.Array (catMaybes)
import Data.Function.Memoize (memoize)
import Data.Generic.Rep (class Generic)
import Data.Generic.Rep.Show (genericShow)
import Data.Int (fromString)
import Data.Int.Bits (complement, shl, shr, (.&.), (.|.))
import Data.Map (Map)
import Data.Map as M
import Data.Maybe (Maybe(..))
import Data.String (Pattern(..), split, trim)
import Data.Traversable (sequence)
import Data.Tuple (Tuple(..))
data Wire
= Constant Int
| Link String
| And String String
| Or String String
| Not String
| LShift String Int
| RShift String Int
derive instance genericWire :: Generic Wire _
instance showWire :: Show Wire where
show = genericShow
input :: String
input = """
...
"""
input' :: Map String Wire
input' = M.fromFoldable <<< catMaybes $ sequence <$> catMaybes do
arr <- map trim <<< split (Pattern "->") <$> (split (Pattern "\n") $ trim input)
pure case arr of
[expr, key] -> Just (Tuple key $ wireExpr expr)
_ -> Nothing
where wireExpr :: String -> Maybe Wire
wireExpr s = case split (Pattern " ") s of
[a, "AND", b] -> Just $ And (trim a) (trim b)
[a, "OR", b] -> Just $ Or (trim a) (trim b)
[a, "LSHIFT", b] -> LShift (trim a) <$> fromString b
[a, "RSHIFT", b] -> RShift (trim a) <$> fromString b
["NOT", a ] -> Just $ Not (trim a)
[a ] -> case fromString a of
Just i -> Just $ Constant i
Nothing -> Just $ Link a
_ -> Nothing
eval :: Map String Wire -> String -> Int
eval m k = case memoize (flip M.lookup m) k of
Just (Constant i ) -> i
Just (Link a ) -> eval m a
Just (And a b) -> eval m a .&. eval m b
Just (Or a b) -> eval m a .|. eval m b
Just (Not a ) -> complement $ eval m a
Just (LShift a i) -> shl (eval m a) i
Just (RShift a i) -> shr (eval m a) i
Nothing -> 0
part1 :: Int
part1 = memoize (eval input') "a"
part2 :: String
part2 = "TODO"
Which works fine on made-up “instruction” input and the example. But on the long puzzle input, it takes forever. As you can see I tried using memoize
, but it doesn’t seem to improve anything. I also tried using trace
from the debug
module, but it didn’t print out anything in the eval
recursive function, instead it pushes Purescript to error out, I didn’t save the error.
So, I’m pretty sure this is because Purescript is not a lazy language, I just don’t know how to make it work so any help is appreciated.
Thanks!