0

So I'm working on a function to find some valid arithmetic operations to a target number from an int list. It's not allowed to use throw/callac. Only add and mul are valid arithmetic operations here and they are left associative.

datatype operation = ADD | MULT

(* find_op: int -> int list -> (operatino list -> 'a) -> (unit -> 'a) -> 'a *)
fun find_op x [] s k = k()
  | find_op x [y] s k = if x=y then s([]) else k()
  | find_op x (y1::y2::ys) s k =
    let
      val add = find_op x ((y1+y2)::ys) (fn a => s(ADD::a)) k
      val mul = find_op x ((y1*y2)::ys) (fn a => s(MULT::a)) k
    in
      need some work here
    end

The function should work like below:

Given list [1,1,2,~1] and target number ~4, the accpeted operation list should be [ADD,ADD,MULT] or [ADD,MULT,MULT], because (((1+1)+2)*~1) = ((1+1)2~1) = ~4. But [MULT,ADD,MULT] will not be valid since (((1*1)+2)*~1) = ~3.

I'm confused how to check whether returned results are k(). Using = to check return value is not possible since it is polymorphic. Is there any method to handle this?

EBADF
  • 641
  • 5
  • 10
  • It's not very clear to me what this function should accomplish, but I'm pretty sure the solution lies in "decorating" the continuations `k` sent to the recursive calls to `find_op`. Most likely, the `k` passed for `add` needs to compute `mult`. Not sure what should happen next. – Ionuț G. Stan Feb 23 '15 at 14:29
  • but k has type unit -> 'a, it cannot take arguments, isn't it? – EBADF Feb 23 '15 at 16:32
  • Indeed, it cannot. Can you show some example input and ouput for this function you're trying to write? Maybe I can figure out what you're trying to do. – Ionuț G. Stan Feb 23 '15 at 16:38
  • ok, I added one example. hope it's clear enough. – EBADF Feb 23 '15 at 16:46

1 Answers1

1

What you have to do is use the two strategies, first try reducing the numbers via ADD, then reduce the numbers via MULT, but sequentially. In order to do this you need to provide a custom failure continuation (k) to the result of the first chosen strategy. If that strategy fails, you try the second strategy in the continuation failure.

You can't try both strategies at the same time and have them both succeed. The function type does not permit returning multiple correct answers. For that you'd need the success continuation's type to be operation list list.

datatype operation = ADD | MULT

fun opToString ADD  = "ADD"
  | opToString MULT = "MULT"

(* find_op: int -> int list -> (operation list -> 'a) -> (unit -> 'a) -> 'a *)
fun find_op x [] s k               = k ()
  | find_op x [y] s k              = if x = y then s [] else k ()
  | find_op x (y1 :: y2 :: ys) s k =
    let
      (* You need a custom failure continuation that tries the MULT variant
       * if the ADD one fails.
       *)
      fun whenAddFails () =
        find_op x ((y1 * y2) :: ys) (fn a => s (MULT :: a)) k
      val add =
        find_op x ((y1 + y2) :: ys) (fn a => s (ADD :: a)) whenAddFails
    in
      add
    end


fun test () =
  let
    val opList = [1,1,2,~1]
    val target = ~4
    fun success ops =
      "success: " ^ (String.concatWith " " (List.map opToString ops))
    fun failure () =
      "couldn't parse numbers as an operation list"
  in
    find_op target opList success failure
  end
Ionuț G. Stan
  • 176,118
  • 18
  • 189
  • 202
  • Your answer is awesome. I've been stucking on this problem for two days and I never thought using k() in that way. Continuation is so tricky for me. Thanks so much! – EBADF Feb 23 '15 at 17:25