Moscow ML - several if statements in one function - if-statement

i'm having some trouble with one part of a function. My function needs an input string of at least 3 characters to avoid error, to do this a want to add one or two "." in the var. Looks something like this:
fun function(a, b) =
if size(a) < 2 then a ^ " " else if size(a) < 3 then a ^ " "
if size(b) < 2 then b ^ " " else if size(b) < 3 then b ^ " "
the function code;
my question is, how do i end the first if line? as it is now the secound if statement get an error.
thanks / joakim

Firstly, this doesn't make any sense. An if-then-else must have all three parts: you can not omit the else, which your trailing if-then clearly does.
Secondly, multiple statements (separated by ;) are only useful when there are side-effects, which there are not. You may change your code to
fun function (a, b) =
( if size a < 2 then a ^ " " else
if size a < 3 then a ^ " " else
a
; if size b < 2 then b ^ " " else
if size b < 3 then b ^ " " else
b
)
but the result of the first statement will be discarded, and is completely useless.
Perhaps you want something more like
fun padLeft (n, a) =
if size a < n
then a ^ CharVector.tabulate(n - size a, fn _ => #" ")
else a
fun function1 (a, b) = (padLeft (3, a), padLeft (3, b))
fun function2 (a, b) = (print (padLeft (3, a)); print (padLeft (3, b)))
where function1 returns a pair using both inputs, and function2 returns unit but has a visible side-effect using both inputs.

Related

Can we use an assert statement inside a function in OCaml?

I wrote a simple recursive fibonacci program that works fine without an assert statement, but when I add an assert statement, even with various permutations of parentheses, spaces, and double semicolons, I keep getting a syntax error during compilation.
Working function:
let rec fib n =
if n = 1
then 1
else
n*(fib (n-1))
Not working:
let rec fib n =
assert (n>=0)
if n = 1
then 1
else
n*(fib (n-1))
Any thoughts appreciated.
Thanks.
You have two expressions: assert (n >= 0) and if ... then ... else .... If you want the two expressions to be evaluated in sequence (which you do), you need to separate them with a semicolon:
let rec fib n =
assert (n >= 0);
if n = 1 then 1
else n * fib (n - 1);;
val fib : int -> int = <fun>
# fib 3;;
- : int = 6
# fib (-3);;
Exception: Assert_failure ("//toplevel//", 2, 4).
Extra spaces do not affect the semantics of OCaml programs. There are also no statements per se in OCaml - everything is an expression. To evaluate two expressions in a sequence you may use the semicolon, e.g.,
print_endline "Hello";
assert (1 > 2);
print_endline "World";
You can also use let .. in .. to chain expressions, especially, if you need to the expression values, e.g.,
let x = 1 + 2 in
let y = 3 + 4 in
Format.printf "%d + %d = %d\n" x y (x + y)
Going back to your example, it should be
let rec fib n =
assert (n >= 0);
if n = 1 then 1
else n * fib (n - 1)
P.S. The double semicolons are not really a part of the language but a special input sequence to be used in the interactive toplevel.

How can I see if a function has already been called in OCaml?

I'm trying to save a value in a function in OCaml that I can then access the next time the function is called. Is there a way to do this?
You can't do this in a functional way, as the function would not be pure.
You can use a ref to store the value you want, and mutate it in the function.
For example, this function that computes a + b + X, where X increases by 1 every time the function is called:
let my_function =
let saved_value = ref 0 in
fun a b ->
saved_value := !saved_value + 1; (* Increment the value *)
a + b + !saved_value
let () = Printf.printf "First: %d\n" (my_function 1 2) (* 1 + 2 + 1 == 4 *)
let () = Printf.printf "Second: %d\n" (my_function 1 2) (* 1 + 2 + 2 == 5 *)

Filtering a custom tuple in haskell

Hey guys I'm working on a function that will output a the board of a board game as a string.
Currently I have it working so that I can output the board without any pieces.
I'm trying to add a filter so that if the current (x, y) coordinate is in the list of tuples [Position, Col] where Position is (Int, Int).
If (x, y) is in this list then I want to check the colour Col and then output accordingly.
drawBoardCell :: (Int, Int) ->[(Position, Col)] -> String
drawBoardCell (x, y) pieces = do
let test = filter (\((a,b),_) -> a == x && b == y) pieces
if snd(test) == Black
then " b "
else if snd(test) == White
then " w "
else " . "
This is what i have tried so far and run into the error:
Display.hs:47:14: error:
• Couldn't match expected type ‘(a0, Col)’
with actual type ‘[((Int, Int), Col)]’
• In the first argument of ‘snd’, namely ‘(test)’
In the first argument of ‘(==)’, namely ‘snd (test)’
In the expression: snd (test) == Black
Display.hs:49:19: error:
• Couldn't match expected type ‘(a1, Col)’
with actual type ‘[((Int, Int), Col)]’
• In the first argument of ‘snd’, namely ‘(test)’
In the first argument of ‘(==)’, namely ‘snd (test)’
In the expression: snd (test) == White
filter returns a list, but snd expects a tuple. These are different types.
You could tweak your conditional as
if snd(head test) == Black
then " b "
else if snd(head test) == White
then " w "
else " . "
but what if test is an empty list, []?
You could code this as
if not (null test) && snd(head test) == Black
then " b "
else not (null test) && if snd(head test) == White
then " w "
else " . "
but this is not a very nice Haskell. Instead, we can get a bit better code with pattern matching:
case test of
((_,Black):_) -> " b "
((_,White):_) -> " w "
_ -> " . "
Or restructure your code to use
find :: (a -> Bool) -> [a] -> Maybe a
instead of the
filter :: (a -> Bool) -> [a] -> [a]
You can use Hoogle to find out more.
The problem is that test is still a list [(Position, Col)], so it can't be passed into snd which expects a single tuple (a, b). If you can guarantee that there will be a match in your pieces array, then you should change your body to:
drawBoardCell (x, y) pieces =
let [match] = filter (\((a,b),_) -> a == x && b == y) pieces
case match of
(_, Black) -> " b "
(_, White) -> " w "
_ -> " . "
Note the use of case here instead of if. It's not strictly necessary, but it's cleaner when comparing a value against multiple options to match this way.
If, however, as I suspect is the case, you might have 0 or 1 matches, you need to use something like the following instead:
drawBoardCell (x, y) pieces = do
case filter (\((a,b),_) -> a == x && b == y) pieces of
[(_, Black)] -> " b "
[(_, White)] -> " w "
_ -> " . "
This will print " . " if the value is not present in the filtered list, if the value is present multiple times, or if the Col of the single match is neither White nor Black.
It is pointed out in the comments that you may want to handle no matches and multiple matches differently. In this case:
drawBoardCell (x, y) pieces = do
case filter (\((a,b),_) -> a == x && b == y) pieces of
[(_, Black)] -> " b "
[(_, White)] -> " w "
[] -> " . " -- Case of no matches
_ -> error "Multiple pieces cannot occupy the same position " ++ show (x, y)
This example will cause a Runtime error if the list has multiple matches (which can be useful for debugging).

How to print variable in sml?

I'm new to SML and in debugging I usually print out the variable to see if it matches up to what I expected it should be.
I would like to print out a variable within a function
what i have is this :
function header..
let val prod
val prod = c + x * y;
in
(print "product "; prod mod 10):: (multiplyAux (prod div 10) xs y)
end;
Right now its printing the string product, but I would like to be able to print the variable prod itself.
The only thing that print can print is strings. So to print a numeric value, it must first be converted to a string. For example:
- print("product " ^ (Int.toString (43 mod 5)) ^ "\n");
product 3
val it = () : unit
Note that Int.toString uses the curried function syntax (i.e. it does not require a tuple for it's argument) so the parenthesis around 43 mod 5 are to make the value clear not to make Int.toString work.
- Int.toString 5;
val it = "5" : string

SML - Incrementing a value in a tuple during foldl that needs to be returned

I'm having a problem while trying to increment my value of x inside the inner foldl call. I make x equal to shiftValue that's passed in and attempt to increment it whenever I find a #" " or #"*" in the inner foldl call, but the value of x that gets returned is always the same as shiftvalue was when passed in.
The function takes in a tuple of (string, int) where the string will have leading spaces and asterisk chopped off that come before any other characters. Also any spaces or asterisk on the end not followed by any other characters will get chopped off. The int that is passed in is a shiftValue that tracks how many spaces the string was shifted over before getting passed into this function. Whenever I take off a leading space or asterisk I need to increment the shiftValue "x" by one.
The inner foldl call removes asterisks and spaces from the front. The outer foldl call removes them from the back. The asterisks and spaces get removed right, the x value just isn't getting updated.
(*Take string str and get rid of leading and following #"*"s and #" "s. For every
leading #"*" or #" " removed increment the shiftValue returned in the tuple*)
fun trimStarsOnNode (str, shiftValue) =
let
val x = shiftValue
in
((implode(rev (foldl (fn (cur, a) =>
if length a = 0 andalso cur = #"*" then a # []
else
if length a = 0 andalso cur = #" " then a # []
else a # [cur]) [] (rev (foldl (fn (cur, a) =>
if length a = 0 andalso cur = #"*" then (x = x + 1; a # [])
else
if length a = 0 andalso cur = #" " then (x = x + 1; a # [])
else a # [cur]) [] (explode str)))))), x)
end;
trimStarsOnNode ("***hello", 3); (* Should print out ("hello", 6) *) but prints out ("hello", 3)
Look at your x - in the beginning of your function, you do:
val x = shiftValue
Then, later, you try to do this:
x = x + 1
Remember, in SML, you can't change the value of a variable (actually, they're just called values in SML, for that reason). x = x + 1 just compares x and x + 1, so the value of the statement x = x + 1 is boolean false.
As Tayacan says, variables are not mutable in SML. If you want mutability you need to use reference types -- but usually, they are best avoided and it's preferable to stick to functional style.
It's also worth noting that your function is going to be very inefficient (O(n^2)), because of your use of list concatenation and length on every iteration. And it is incorrect, because it will also remove stars in the middle of the string (and then redundantly go over the whole list a second time). Finally, your solution is far too complicated.
FWIW, here is the shortest implementation I can think of, using the Substring library module and the function composition operator o:
fun isStarOrSpace c = (c = #"*" orelse c = #" ")
val trimStars =
let open Substring
in string o dropl isStarOrSpace o dropr isStarOrSpace o full end
This does not use your shiftValue because I don't understand what it's supposed to do. You can easily compute the number of removed characters by comparing the old and new string size. That is, your intended function (IIUC) could easily be expressed on top of mine as
fun trimStarsOnNode(s, shift) =
let val s' = trimStars s in (s', size s - size s' + shift) end
But to be honest, I don't understand what this version would be good for.
Edit: A version that returns the left drop count:
fun trimStars s =
let
open Substring
val ss = dropl isStarOrSpace (dropr isStarOrSpace (full s))
in
(string ss, #2(base ss))
end