'OCaml task, Contagion, issues with syntax [closed]

This is the task:

Write a function onstepcontagion : bool array array -> bool array array = <fun> that given a rectangular bool matrix, where true represents an infected square and false represent non-infected square it calculates the next step of infection. Infected squares remain infected indefinitely, non-infected squares become infected if the they are vertically/horizontally adjacent to at least two other infected squares

This is my code so far:

    let printmat matrix =
      let n = Array.length matrix in
      let n1 = Array.length matrix.(0) in
      for i = 0 to n - 1 do 
        for j = 0 to n1 - 1 do
          if matrix.(i).(j) == true then print_string"1"
          else print_string"0";
        done; 
        print_string "\n";
      done;;
      
      let onstepcontagion matrix =
      let n = Array.length matrix in
      let n1 = Array.length matrix.(0) in
      for i = 0 to n - 1 do
        for j = 0 to n1 - 1 do
            let tmp = 0 in
            if (j < n1-2) then
               let right = if matrix.(i).(j+1) = true then 1 else 0 in
            if (j > 0) then
                let left = if matrix.(i).(j-1) = true then 1 else 0 in
            if (i < n-2) then
                let up = if matrix.(i-1).(j) = true then 1 else 0 in
            if (i > 0) then
                let down = if matrix.(i+1).(j) = true then 1 else 0 in
            let sum = right + left + up + down in
            if sum > 1 then print_string "JTB "
            else print_string "0" 
        done; 
      done;;

I have no errors, but no output when I call the function. I am very confused regarding the syntax and function of OCaml, I am a total beginner and still in learning phase. I wrote my task in C, I was trying to implement a similar solution in ocaml.

C code:


    #include <stdio.h>
    #include <stdlib.h>
    
    void print_mat (int n, int m, int arr[n][m])
    {
        int i,j;
        for (i = 0; i < n; i++){
            for (j = 0; j < m; j++)
                printf("%d ",arr[i][j]);
            printf("\n");
        }
        printf("\n\n");
    }
    
    
    int main()
    {
        int n, m, i, j, tmp, changes = 1;
        printf("input Mat len\n");
        scanf("%d%d",&n,&m);
        int arr[n][m];
        int cpy[n][m];
        for (i = 0; i < n; i++)
            for (j = 0; j < m; j++){
            scanf("%d",&tmp);
            if (tmp == 0)
                arr[i][j] = tmp;
            else
                arr[i][j] = 1;
            }
        while (changes){
            changes = 0;
        for (i = 0; i < n; i++){
            for (j = 0; j < m; j++)
            {
                tmp = 0;
                if (arr[i][j] != 1){
                    if (j < m-1)
                        if (arr[i][j+1] == 1)
                            tmp++;
                    if (j > 0)
                        if (arr[i][j-1] == 1)
                            tmp++;
                    if (i < n-1)
                        if (arr[i+1][j] == 1)
                            tmp++;
                    if (i > 0)
                        if (arr[i-1][j] == 1)
                            tmp++;
                    if (tmp > 1){
                        cpy[i][j] = 1;
                        changes = 1;
                    }
                }
            }
            }
            if (changes == 1){
             for (i = 0; i < n; i++)
                for (j = 0; j < m; j++)
                if (arr[i][j] == 1 || cpy[i][j] == 1)
                    arr[i][j] = 1;
               printf("\n");
               print_mat(n,m, arr);
            }
        }
        return 0;
    }
    //010  100  001  000

I would appreciate if someone could give me a hand here. I learn best trough examples, and I am unable to find similar solved tasks to learn from.



Solution 1:[1]

Your printmat function works for me:

# printmat [|[|true;false|];[|false;true|]|];;
10
01
- : unit = ()

In your onstepcontagion the indentation isn't accurate; i.e., it doesn't show the nested structure of the if tests.

If I run onstepcontagion on a 4x4 matrix of all false, I see an indexing error:

# onstepcontagion (Array.init 4 (fun _ -> Array.make 4 false));;
Exception: Invalid_argument "index out of bounds".

On this line:

let up = if matrix.(i-1).(j) = true then 1 else 0 in

You haven't yet verified that i > 0, so it will fail when i = 0.

(However, I'm not sure the code is actually doing what you wanted, because the indentation is incorrect.)

You should show your call to onstepcontagion so SO commenters can try the same test as you. If your matrix is too small, the function indeed does nothing much (as at least one of the ifs will be false).

Solution 2:[2]

To augment Jeffrey's answer, your onstepcontagion function formatted better to reflect the nesting of the conditions and with extraneous parentheses removed.

let onstepcontagion matrix =
  let n = Array.length matrix in
  let n1 = Array.length matrix.(0) in
  for i = 0 to n - 1 do
    for j = 0 to n1 - 1 do
      let tmp = 0 in
      if j < n - 2 then
        let right = if matrix.(i).(j + 1) = true then 1 else 0 in
        if j > 0 then
          let left = if matrix.(i).(j - 1) = true then 1 else 0 in
          if i < n - 2 then
            let up = if matrix.(i - 1).(j) = true then 1 else 0 in
            if i > 0 then
              let down = if matrix.(i + 1).(j) = true then 1 else 0 in
              let sum = right + left + up + down in
              if sum > 1 then print_string "JTB "
              else print_string "0" 
    done; 
  done

Since nothing happens unless all of these conditions are true, you might streamline this with something like the following. Note that tmp appears to serve no purpose, as has thus been elided.

let onstepcontagion matrix =
  let n  = Array.length matrix in
  let n1 = Array.length matrix.(0) in
  for i = 0 to n - 1 do
    for j = 0 to n1 - 1 do
      if j < n - 2 && j > 0 && i < n - 2 && i > 0 then
        let right = if matrix.(i).(j + 1) then 1 else 0 in
        let left  = if matrix.(i).(j - 1) then 1 else 0 in
        let up    = if matrix.(i - 1).(j) then 1 else 0 in
        let down  = if matrix.(i + 1).(j) then 1 else 0 in
        let sum   = right + left + up + down in
        if sum > 1 then 
          print_string "JTB "
        else 
          print_string "0" 
    done; 
  done

We could also introduce a int_of_bool function.

let onstepcontagion matrix =
  let int_of_bool = function true -> 1 | _ -> 0 in
  let n           = Array.length matrix in
  let n1          = Array.length matrix.(0) in
  for i = 0 to n - 1 do
    for j = 0 to n1 - 1 do
      if j < n - 2 && j > 0 && i < n - 2 && i > 0 then
        let right = int_of_bool matrix.(i).(j + 1) in
        let left  = int_of_bool matrix.(i).(j - 1) in
        let up    = int_of_bool matrix.(i - 1).(j) in
        let down  = int_of_bool matrix.(i + 1).(j) in
        let sum   = right + left + up + down in
        if sum > 1 then 
          print_string "JTB "
        else 
          print_string "0" 
    done; 
  done

Thank you for clarifying your purpose in comments. As I understand it now, you are checking to see if any two horizontal or vertical neighbors are true.

As written, your code checks to ensure that matrix elements have four vertical/horizontal neighbors. This rules out corner and edge elements. The sample data you've shown is a 2x2 matrix. No part of that matrix has more than two neighbors, so the condition is never true.

You have demonstrated imperative thinking in how you've constructed your code (and in how you've indented this). Each if does not define a variable and then move onto the next. There are no if statements in OCaml but rather if expressions.

if a then ... else ...

The else can be elided only if the return value of the expression is unit.

Your set of conditional expressions has formed one big expression. If the first condition is not true, the whole thing is not evaluated.

So the real question becomes, how do we handle a matrix "cell" not having a particular neighbor? Well, what happens if we try to access an array element out of bounds?

utop # let arr = [| 1; 2; 3; 4 |] in arr.(4);;
Exception: Invalid_argument "index out of bounds".

We get an exception. But we can handle exceptions.

An out of bounds exception in this case would be equivalent to finding 0 in that "cell", so we can handle each access by returning 0 if such an exception occurs.

We also now don't need to do any explicit bounds-checking and every "cell" gets evaluated.

let onstepcontagion matrix =
  let int_of_bool = function true -> 1 | _ -> 0 in
  let n           = Array.length matrix in
  let n1          = Array.length matrix.(0) in
  for i = 0 to n - 1 do
    for j = 0 to n1 - 1 do
      let right = try int_of_bool matrix.(i).(j + 1) with Invalid_argument _ -> 0 in
      let left  = try int_of_bool matrix.(i).(j - 1) with Invalid_argument _ -> 0 in
      let up    = try int_of_bool matrix.(i - 1).(j) with Invalid_argument _ -> 0 in
      let down  = try int_of_bool matrix.(i + 1).(j) with Invalid_argument _ -> 0 in
      let sum   = right + left + up + down in
      if sum > 1 then 
        print_string "JTB "
      else 
        print_string "0" 
    done; 
  done

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2