prichey
prichey

Reputation: 75

OCaml: retain value of variable with control statements

I'm very new to OCaml / functional programming, and I'm confused about the implementation of some things that are relatively simple other languages I know. I could use any and all help.

Chiefly: in a program I'm working on, I either increment or decrement a variable based on a certain parameter. Here's something representative of what I have:

let tot = ref 0 in
for i = 0 to s do
    if test_num > 0 then 
        tot := !tot + other_num
    else
        tot := !tot - other_num
done;;

This is obviously not the way to go about it, because even if the else statement is never taken, the code acts as if it is, each and every time, presumably because it's closer to the bottom of the program? I know OCaml has pretty sophisticated pattern matching, but within this level of coed I need access to a handful of lists I've already created, and, as far as I understand, I can't access those lists from a top-level function without passing them all as parameters.

I know I'm going about this the wrong way, but I have no idea how to do this idiomatically.

Suggestions? Thanks.

edit Here's a more concise example:

let ex_list = [1; -2; 3; -4] in
let max_mem = ref 0 in
let mem = ref 0 in
let () = 
    for i = 0 to 3 do
        let transition = List.nth ex_list i in
        if transition > 0 then (
            mem := (!mem + 10);
        ) else 
            mem := (!mem - 1);
        if (!mem > !max_mem) then (max_mem := !mem);
    done;
print_int !max_mem; print_string "\n";
in !mem;

At the end, when I print max_mem, I get 19, though this value should be (0 + 10 - 1 + 10 - 1 = 18). Am I doing the math wrong, or does the problem come from somewhere else?

Upvotes: 0

Views: 219

Answers (2)

Jeffrey Scofield
Jeffrey Scofield

Reputation: 66818

Your code looks fine to me. It doesn't make a lot of sense as actual code, but I think you're just trying to show a general layout. It's also written in imperative style, which I usually try to avoid if possible.

The if in OCaml acts just like it does in other languages, there's no special thing about being near the bottom of the program. (More precisely, it acts like the ? : ternary operator from C and related languages; i.e., it's an expression.)

Your code doesn't return a useful value; it always returns () (the quintessentially uninteresting value known as "unit").

If we replace your free variables (ones not defined in this bit of code) by constants, and change the code to return a value, we can run it:

# let s = 8 in
let other_num = 7 in
let test_num = 3 in
let tot = ref 0 in
let () =
    for i = 0 to s do
        if test_num > 0 then
            tot := !tot + other_num
        else
            tot := !tot - other_num
    done
in
!tot;;
- : int = 63
#

If you're trying to learn to write in a functional style (i.e., without mutable variables), you would write this loop as a recursive function and make tot a parameter:

# let s = 8 in
let other_num = 7 in
let test_num = 3 in
let rec loop n tot =
    if n > s then 
        tot
    else 
        let tot' =
            if test_num > 0 then tot + other_num else tot - other_num
        in
        loop (n + 1) tot'
in
loop 0 0;;
- : int = 63

It would probably be easier to help if you gave a (edited to add: small :-) self-contained problem that you're trying to solve.

The other parts of your question aren't clear enough to give any advice on. One thing that I might point out is that it's completely idiomatic to use pattern matching when processing lists.

Also, there's nothing wrong with passing things as parameters. That's why the language is called "functional" -- your code consists of functions, which have parameters.

Update

I like to write let () = expr1 in expr2 instead of expr1; expr2. It's just a habit I got into, sorry if it's confusing. The essence is that you're evaluating the first expression just for its side effects (it has type unit), and then returning the value of the second expression.

If you don't have something after the for, the code will evaluate to (), as I said. Since the purpose of the code seems to be to compute the value of !tot, this is what I returned. At the very least, this lets you see the calculated value in the OCaml top level.

tot' is just another variable. If you calculate a new value straightforwardly from a variable named var, it's conventional to name the new value var'. It reads as "var prime".

Update 2

Your example code works OK, but it has the problem that it uses List.nth to traverse a list, which is a slow (quadratic) operation. In fact your code is naturally considered a fold. Here's how you might write it in a functional style:

# let ex_list = [1; -2; 3; -4] in
let process (tot, maxtot) transition =
    let tot' = if transition > 0 then tot + 10 else tot - 1 in
    (tot', max maxtot tot')
in
List.fold_left process (0, 0) ex_list;;
- : int * int = (18, 19)
#

Upvotes: 2

Andreas Rossberg
Andreas Rossberg

Reputation: 36088

In addition to Jeffrey's answer, let me second that this is not how you would usually write such code in Ocaml, since it is a very low-level imperative approach. A more functional version would look like this:

let high ex_list =
  let deltas = List.map (fun x -> if x > 0 then 10 else -1) ex_list in
  snd (List.fold_left (fun (n, hi) d -> (n+d, max (n+d) hi)) (0, 0) deltas)

let test = high [1; -2; 3; -4]

Upvotes: 0

Related Questions