Hi,
I just started out learning OCaml, so I apologise if this is too basic a question for this Discourse.
In an effort to get a grip on OCaml’s handling of laziness, I wanted to implement the min coin change problem. Here is a basic solution in Haskell:
import Data.List
import Data.Array
change :: [Int] -> Int -> [Int]
change coins value = memo ! value
where
memo :: Array Int [Int]
memo = array (0, value) [(n, getVal n) | n <- [0 .. value]]
getVal :: Int -> [Int]
getVal 0 = []
getVal v = uncurry (:) . head . sortOn (length . snd) $
[ (i, memo ! (v - i)) | i <- takeWhile (<= v) coins ]
main :: IO ()
main = print $ change [1, 4, 15, 20, 50] 23
I thought this was a nice problem because the code is still pretty basic, but it prominently features mutual recursion of memo
and getVal
. I haven’t managed to implement this in OCaml, hence this cry for help. My basic attempt was something along the lines of
open Core
let change coins value =
let rec memo = lazy (Array.of_list_map (List.range 0 value) ~f:get_val)
and get_val = function
| 0 -> []
| v -> List.(sort (map ~f:(fun i -> (i, memo.(v - i)))
(* ^^^^^ argh *)
(take_while coins ~f:(fun x -> x <= v)))
~compare:(fun (_, a) (_, b) ->
Int.compare (List.length a) (List.length b)))
|> (fun xs -> let (a, b) = List.hd_exn xs in List.cons a b)
in (Lazy.force memo).(value)
but of course this doesn’t work, because of the marked spot; memo
is lazy, and would need to be forced in order for its value to be usable. The computation of get_val
itself should be lazy, so forcing a value there doesn’t sound advisable—and an Exception: Lazy.Undefined.
upon evaluation seems to agree with me. However, even if get_val
was lazy, I feel like the compiler would still force me to evaluate memo
here, because I’m accessing a value via the array syntactic sugar, and laziness is being kept track of at the type level. This leaves me a bit confused, and unsure what to do.