classoopmatrixocamlsudoku

Insert function for a matrix in OCaml is not working


class virtual ['a] matrix_game m pf =
  object 
    val matrix : 'a array array = m
    val rows = Array.length m
    val cols = Array.length m.(0)
    val print_function : 'a -> unit = pf

    method print =
      for i = (-1) to cols do 
        print_char '-' 
      done;
      print_char '\n';
      Array.iter 
        (fun x ->
           print_char '|';
           Array.iter (fun y -> print_function y) x;
           print_endline "|") 
        matrix;
      for i = (-1) to cols do 
        print_char '-' 
      done;
      print_char '\n'

    method private is_out_of_bounds (y, x) = 
      y >= rows || y < 0 || x >= cols || x < 0

    method virtual private is_legal : int * int -> 'a -> bool
    method virtual insert : int * int -> 'a -> unit
  end

class sudoku =
  object(self)
    inherit ['a] matrix_game (Array.make_matrix 9 9 0) (fun x -> print_char ' ') as parent
   
   val m = Array.make_matrix 9 9 0 (*override first def of matrix*)
    method private is_out_of_bounds (y, x) =
      y >= 9 || y < 0 || x >= 9 || x < 0

    method private check_value v = v > 0 && v < 10

    method private check_existence (y, x) v =
      let empty_value = 0 in
      let row_length = Array.length m in
      let col_length = if row_length > 0 then Array.length m.(0) else 0 in
      if y >= 0 && y < row_length && x >= 0 && x < col_length then
        m.(y).(x) <> empty_value
      else
        false

    method private is_legal (y, x) v =
      not (self#is_out_of_bounds (y, x)) && 
      self#check_value v && 
      not (self#check_existence (y, x) v)

    method insert (y, x) v =
      if self#is_legal (y, x) v then
        m.(y).(x) <- v
      else
        print_endline "Illegal move!"

    method print : unit =
      parent#print
  end

methods insert and print are not working.

let s1 = new sudoku ;; 

s1#insert (3,3) 5 ;; 

s1#print;; 

it should insert 5 to (3,3) in the matrix and print it. I'm stuck on this part. Sorry if this is dumb but any help is appreciated.


Solution

  • If we add a very simple method get_m as follows to sudoku, we can see that the state is being updated.

    method get_m = m
    
    # let s1 = new sudoku in s1#insert (3, 3) 5; s1#get_m;;
    - : int array array =
    [|[|0; 0; 0; 0; 0; 0; 0; 0; 0|]; [|0; 0; 0; 0; 0; 0; 0; 0; 0|];
      [|0; 0; 0; 0; 0; 0; 0; 0; 0|]; [|0; 0; 0; 5; 0; 0; 0; 0; 0|];
      [|0; 0; 0; 0; 0; 0; 0; 0; 0|]; [|0; 0; 0; 0; 0; 0; 0; 0; 0|];
      [|0; 0; 0; 0; 0; 0; 0; 0; 0|]; [|0; 0; 0; 0; 0; 0; 0; 0; 0|];
      [|0; 0; 0; 0; 0; 0; 0; 0; 0|]|]
    

    So why does that not reflect when you print?

    Well, let's add a few more methods to test this. The updated code is:

    class virtual ['a] matrix_game m pf =
      object 
        val matrix : 'a array array = m
        val rows = Array.length m
        val cols = Array.length m.(0)
        val print_function : 'a -> unit = pf
    
        method print =
          for i = (-1) to cols do 
            print_char '-' 
          done;
          print_char '\n';
          Array.iter 
            (fun x ->
               print_char '|';
               Array.iter (fun y -> print_function y) x;
               print_endline "|") 
            matrix;
          for i = (-1) to cols do 
            print_char '-' 
          done;
          print_char '\n'
    
        method private is_out_of_bounds (y, x) = 
          y >= rows || y < 0 || x >= cols || x < 0
    
        method virtual private is_legal : int * int -> 'a -> bool
        method virtual insert : int * int -> 'a -> unit
    
        method get_matrix = matrix
      end
    
    class sudoku =
      object(self)
        inherit ['a] matrix_game (Array.make_matrix 9 9 0) (fun x -> print_char ' ') as parent
       
       val m = Array.make_matrix 9 9 0 (*override first def of matrix*)
        method private is_out_of_bounds (y, x) =
          y >= 9 || y < 0 || x >= 9 || x < 0
    
        method private check_value v = v > 0 && v < 10
    
        method private check_existence (y, x) v =
          let empty_value = 0 in
          let row_length = Array.length m in
          let col_length = if row_length > 0 then Array.length m.(0) else 0 in
          if y >= 0 && y < row_length && x >= 0 && x < col_length then
            m.(y).(x) <> empty_value
          else
            false
    
        method private is_legal (y, x) v =
          not (self#is_out_of_bounds (y, x)) && 
          self#check_value v && 
          not (self#check_existence (y, x) v)
    
        method insert (y, x) v =
          if self#is_legal (y, x) v then
            m.(y).(x) <- v
          else
            print_endline "Illegal move!"
    
        method print : unit =
          parent#print
    
        method get_m = m
        method get_matrix = parent#get_matrix
      end
    

    Now if we test this:

    # let s1 = new sudoku;;
    val s1 : sudoku = <obj>
    # s1#insert (3, 3) 5;;
    - : unit = ()
    # s1#get_m;;
    - : int array array =
    [|[|0; 0; 0; 0; 0; 0; 0; 0; 0|]; [|0; 0; 0; 0; 0; 0; 0; 0; 0|];
      [|0; 0; 0; 0; 0; 0; 0; 0; 0|]; [|0; 0; 0; 5; 0; 0; 0; 0; 0|];
      [|0; 0; 0; 0; 0; 0; 0; 0; 0|]; [|0; 0; 0; 0; 0; 0; 0; 0; 0|];
      [|0; 0; 0; 0; 0; 0; 0; 0; 0|]; [|0; 0; 0; 0; 0; 0; 0; 0; 0|];
      [|0; 0; 0; 0; 0; 0; 0; 0; 0|]|]
    s1#get_matrix;;
    - : int array array =
    [|[|0; 0; 0; 0; 0; 0; 0; 0; 0|]; [|0; 0; 0; 0; 0; 0; 0; 0; 0|];
      [|0; 0; 0; 0; 0; 0; 0; 0; 0|]; [|0; 0; 0; 0; 0; 0; 0; 0; 0|];
      [|0; 0; 0; 0; 0; 0; 0; 0; 0|]; [|0; 0; 0; 0; 0; 0; 0; 0; 0|];
      [|0; 0; 0; 0; 0; 0; 0; 0; 0|]; [|0; 0; 0; 0; 0; 0; 0; 0; 0|];
      [|0; 0; 0; 0; 0; 0; 0; 0; 0|]|]
    

    The matrix in the sudoku class object is not the same as the matrix in the parent. Now, since parent#print prints that matrix, the changes are not reflected in your output.

    We can add a print' method which directly prints m:

        method print' =
          for i = (-1) to cols do 
            print_char '-' 
          done;
          print_char '\n';
          Array.iter 
            (fun x ->
               print_char '|';
               Array.iter (fun y -> print_function y) x;
               print_endline "|") 
            self#get_m;
          for i = (-1) to cols do 
            print_char '-' 
          done;
          print_char '\n'
    

    But the problem with this is that `print_function was specified to always print a space, regardless of what was passed in, so we need to fix that.

        inherit ['a] matrix_game (Array.make_matrix 9 9 0) (fun x -> print_int x) as parent
    

    Or just:

        inherit ['a] matrix_game (Array.make_matrix 9 9 0) print_int as parent
    

    Having made these changes:

    # s1#print';;
    -----------
    |000000000|
    |000000000|
    |000000000|
    |000500000|
    |000000000|
    |000000000|
    |000000000|
    |000000000|
    |000000000|
    -----------
    - : unit = ()
    

    Now, the real root of the problem is that m was unnecessary. We could instead replace your code with a simpler version.

    class virtual ['a] matrix_game m pf =
      object 
        val matrix : 'a array array = m
        val rows = Array.length m
        val cols = Array.length m.(0)
        val print_function : 'a -> unit = pf
    
        method print =
          for i = (-1) to cols do 
            print_char '-' 
          done;
          print_char '\n';
          Array.iter 
            (fun x ->
               print_char '|';
               Array.iter (fun y -> print_function y) x;
               print_endline "|") 
            matrix;
          for i = (-1) to cols do 
            print_char '-' 
          done;
          print_char '\n'
    
        method private is_out_of_bounds (y, x) = 
          y >= rows || y < 0 || x >= cols || x < 0
    
        method virtual private is_legal : int * int -> 'a -> bool
        method virtual insert : int * int -> 'a -> unit
    
        method get_matrix = matrix
      end
    
    class sudoku =
      object(self)
        inherit ['a] matrix_game (Array.make_matrix 9 9 0) (fun x -> print_int x) as parent
       
        method private is_out_of_bounds (y, x) =
          y >= 9 || y < 0 || x >= 9 || x < 0
    
        method private check_value v = v > 0 && v < 10
    
        method private check_existence (y, x) v =
          let empty_value = 0 in
          let row_length = Array.length matrix in
          let col_length = if row_length > 0 then Array.length matrix.(0) else 0 in
          if y >= 0 && y < row_length && x >= 0 && x < col_length then
            matrix.(y).(x) <> empty_value
          else
            false
    
        method private is_legal (y, x) v =
          not (self#is_out_of_bounds (y, x)) && 
          self#check_value v && 
          not (self#check_existence (y, x) v)
    
        method insert (y, x) v =
          if self#is_legal (y, x) v then
            matrix.(y).(x) <- v
          else
            print_endline "Illegal move!"
    
        method print : unit =
          parent#print
      end
    

    And testing it:

    # let s1 = new sudoku in
      s1#insert (3, 3) 5;
      s1#print;;
    -----------
    |000000000|
    |000000000|
    |000000000|
    |000500000|
    |000000000|
    |000000000|
    |000000000|
    |000000000|
    |000000000|
    -----------
    - : unit = ()
    

    Printing

    Your printing method can be cleaned up considerably by leveraging the Format module.

    Consider printing an array of integers without spaces between them:

    # let a = [|1; 2; 3; 4; 5; 6; 7; 8; 9|] in
      let pp_nothing ppf () = Format.fprintf ppf "" in
      let pp_int ppf i = Format.fprintf ppf "%d" i in
      Format.asprintf "%a" (Format.pp_print_array ~pp_sep: pp_nothing pp_int) a;;
    - : string = "123456789"
    

    Now, if I want to print that with bars on either side:

    # let a = [|1; 2; 3; 4; 5; 6; 7; 8; 9|] in
      let pp_nothing ppf () = Format.fprintf ppf "" in
      let pp_int ppf i = Format.fprintf ppf "%d" i in
      Format.asprintf "|%a|" (Format.pp_print_array ~pp_sep: pp_nothing pp_int) a;;
    - : string = "|123456789|"
    

    Extending this:

        method print =
          let pp_nothing ppf () = Format.fprintf ppf "" in
          let pp_newline ppf () = Format.fprintf ppf "\n" in
          let pp_int ppf i = Format.fprintf ppf "%d" i in
          let pp_char ppf ch = Format.fprintf ppf "%c" ch in
          let pp_int_arr ppf arr = 
            Format.fprintf ppf "|%a|" 
              (Format.pp_print_array ~pp_sep: pp_nothing pp_int) arr
          in
          let pp_char_arr ppf arr = 
            Format.fprintf ppf "%a" 
              (Format.pp_print_array ~pp_sep: pp_nothing pp_char) arr
          in
          let pp_cap ppf len = 
            let a = Array.make len '-' in
            Format.fprintf ppf "%a" pp_char_arr a
          in
          Format.printf "%a\n%a\n%a\n" 
            pp_cap 11 
            (Format.pp_print_array ~pp_sep: pp_newline pp_int_arr) matrix 
            pp_cap 11