Should an if statement be associated with else statement in ocaml? - if-statement

In ocaml, I want to have many nested if statements and a return value on each of the conditions. The code is becoming complicated like this.
let func arg1 arg2 =
if condition1 then arg1+arg2
else
(
//code1
if condition2 then arg1*arg2
else
(
//code2
if condition3 then arg1+arg2
else
(
//code3
)
)
)
Instead of such nested statements can I have code like this?
let func arg1 arg2 =
if condition1 then arg1+arg2
//code1
if condition2 then arg1*arg2
//code2
if condition3 then arg1+arg2
//code3

You can use an if statement without a else if it returns a value of type unit (basically when it only does something).
if condition then print_int 3
However, in your case, you want to return a value of type int and as such the else branch is mandatory. It can nonetheless be shortened using else if statements.
if condition1 then arg1+arg2
else if condition2 then arg1*arg2
else if condition3 then arg1+arg2
else arg1
Note that once again, you need to use else at the end.
Pattern-matching can also be extended to verify some conditions using when clauses:
match 3 with
| 0 -> 0
| 1 -> 1
| x when x mod 2 = 0 -> x/2
| x when x mod 3 = 0 -> x/3
| x -> x

OCaml is a strongly and statically typed language, which means that the type of every expression is checked at compile-time.
Take a look at the following snippet.
if condition then true_value else false_value
During compilation, the type-checker will check for the following:
condition must have type bool;
true_value must have the same type as false_value;
The whole expression has the same type as true_value and false_value.
If any one of these statements is not true, then the compilation fails with a type error.
Now, let's take a look at a if statement, without a else.
if condition then true_value
If the condition is false, then the expression evaluates to (), the only value of type unit. Using statements 2 and 3 from before, the only type that true_value can have here is unit. That means that you can't use int or string or anything as the true_value.
Usually, deeply nested if-else statements is considered a code smell: it may indicate that your code needs refactoring. For instance, OCaml offers pattern-matching. Depending on what your actual code looks like, it may be the way to go.

Just fyi, you don't need all those parentheses. Anything that comes after an else is considered to be a single expression in the 'else' branch. You could write your code like
let func arg1 arg2 =
if condition1 then arg1+arg2 else
(* code1 *)
if condition2 then arg1*arg2 else
(* code2 *)
if condition3 then arg1+arg2 else
(* code2 *)

Related

How can I improve my Haskell code and make it work?

In my code I call the validateFEN function with a FEN String. A Fen string looks like this for example:
",w84,w41,w56,w170,w56,w41,w84,/,,w24,w40,w17,w40,w48,,/,,,w16,w16,w16,,,/,,,,,,,,/,,,,,,,,/,,,,,,,,/,,,b1,b1,b1,,,/,,b3,b130,b17,b130,b129,,/,b69,b146,b131,b170,b131,b146,b69,"
The function should take the string and check if it's a FEN string.
How does it know if it's a FEN string? -> My string has 9 rows and 9 columns. I don't need to check if the pieces (example: w86) are at their right position.
validateFEN' b = help3 (filter (\x -> x == ',' || x == '/' ) b)
help3 b = (if help1 b == True then (if head (drop 8 b) == '/' then help3 (drop 9 b) else False) else False )
help1 b = help2 (take 8 b)
help2 b = foldr (+) 0 (map (\x -> if x == ',' then 1 else 0 )b) == 8
Why do I keep getting the error for a empty list?
Exception: Prelude.head: empty list
I understand that with my code there is no stop. The program doesn't know when the string is "correct".
Is there a shorter simpler way of doing this?
One way to reuse existing library functions to make this clearer would be to use stripPrefix.
help3 s = case stripPrefix ",,,,,,,,/" s of
Nothing -> False
Just s' -> help3 s'
Of course, you still need to handle the final case, where there is no terminating /. This can be done with a single extra clause:
help3 ",,,,,,,," = True
help3 s = {- ... -}
You might want to ponder whether validateFEN' should have a similar special case for the empty string.
BUT I would strongly suggest simply not implementing validateFEN' in the first place. My guess is that the plan is something like this:
Check if a string is valid FEN.
Process the string, assuming FEN-ness.
Instead, I recommend the following approach:
Parse the string into a native data structure that represents the information available in a FEN string.
Process the native structure.
Step 1, if written with standard parsing solutions, will "accidentally" validate the string -- i.e. running your parser will return something like Either Error FEN, which you can pattern match on to either discover that the string is invalid or that it is valid and has been turned into a more idiomatic representation.

Error SML: unbound variable or constructor

I am new to SML. I tried to create and test the following function below, but I received an error. I do not know what is the problem.
fun isOld(pFirstTuple: int*int*int, pSecondTuple: int*int*int) =
if (#1 pFirstTuple) < (#1 pSecondTuple)
then
true
if (#1 pFirstTuple) = (#1 pSecondTuple)
then
if (#2 pFirstTuple) < (#2 pSecondTuple)
then
true
else
false
I have tried this command "val p = isOld((8,9,10),(10,11,12))", but it showed me the following error Unbound variable or constructor. How do I fix this?
Here's what your code looks like, stripped down by ignoring various subexpressions (replacing them with A, B, and C)
if A
then true
if B
then if C
then true
else false
You're making extensive use of if/then/else, but the syntax is not quite correct. In SML, every if must have both a then and else clause associated with it. Here's my guess at what you actually meant:
if A
then true
else if B
then if C
then true
else false
else false
This is starting to get quite messy---but you can clean it up with boolean logic. Notice, for example, that if X then true else false means exactly the same thing as simply writing X, because both expressions are type bool and will always evaluate to the same boolean, regardless of what X is. You can extend this reasoning to see that
if X then true else Y is equivalent to X orelse Y.
if X then Y else false is equivalent to X andalso Y.
With these ideas, we can clean up your code considerably:
A orelse (B andalso C)

Multiple conditions inside single 'if' in SML

How can multiple conditions be specified under single if? For example, consider the following java code snippet:
if(a==1 && a>b){
//statements ;
}
How can above code be written in sml? I know that I can achieve the goal by using two if's but still if there is a method to specify in the way I want, then it will be smooth.
SML's equivalent of && is andalso. For || there is orelse:
if a = 1 andalso a > b
then (* ... *)
else (* ... *)

Ocaml if-then-else Syntax Error

Why is this Ocaml statement giving me a syntax error?
let a = 0;; if a = 0 then let b = 0;;
Do if then else statements always have to return a value?
EDIT: Here is the code I am struggling with. I want to apply this function over a list with the map function. The function is supposed to look at each word in the list wordlist and add to the stringmap. If it has already been added to the string map then add 1 to its password.
module StringMap = Map.Make(String)
let wordcount = StringMap.empty
let findword testword =
let wordcount = (if (StringMap.mem testword wordcount)
then (StringMap.add testword ((StringMap.find testword wordcount)+1) wordcount)
else (StringMap.add testword 1 wordcount))
List.map findword wordlist
You can only have an if then without else if the then expression evaluates to unit () Otherwise, the expression will not type check. An if without an else is equivalent to writing if x then y else () which can only type check if y is unit.
Check this out for a reference.
(Terminology note: there are no statements in OCaml because everything is an expression, so the term "if statement" doesn't quite apply. I still understood what you meant, but I thought this was worth noting)
Yes, if is an expression in OCaml, not a statement. The best way to look at it is that there are no statements in OCaml. Everything is an expression. (Admittedly there are expressions that return (), which are similar to statements.)
You can only have if b then e if the type of e is unit (i.e., if it returns ()).
Note also that you can't just say let v = e, except at the top level of a module. At the top level it defines a global name in the module. In other cases you need to say let v = e1 in e2; the let defines a local symbol v for use in the expression e2.
One answer to the let b = problem - it works like this:
let a = 0
let b = if a = 0 then 0 else 1
(* or whatever value you need in the else branch *)
And then the Map problem: the manual says Map is applicative - that means Stringmap.add returns a new map. You must use a ref to store your map - see this ocaml toplevel protocol:
# module StringMap = Map.Make(String);;
# let mymap = ref StringMap.empty ;;
val mymap : '_a StringMap.t ref = {contents = <abstr>}
# mymap := StringMap.add "high" 1 !mymap;;
- : unit = ()
# StringMap.mem "high" !mymap;;
- : bool = true
# StringMap.mem "nono" !mymap;;
- : bool = false
# StringMap.find "high" !mymap;;
- : int = 1
# StringMap.find "nono" !mymap;;
Exception: Not_found.

How do I make a function that writes data into a file give boolean output?

I have to write a code that takes integer list and writes them to a file. At the same time it returns true if the element is written, else false if it is not written.
I wrote something like
fun writetofile([],sfile)= false
|writetofile((l:int)::ls, sfile)=
let
val outs=TextIO.openOut(sfile)
fun writeinline(outs,[])=(false;TextIO.closeOut(outs))
|writeinline(outs,(l:int)::ls)=(true;TextIO.output(outs,(Int.toString(l)^"\n"));writeinline(outs,ls))
in
writeinline(outs,l::ls) before
TextIO.closeOut(outs)
end
;
I got following error:
Error: right-hand-side of clause doesn't agree with function result type [tycon mismatch]
expression: unit
result type: bool
in declaration:
writeNums =
(fn (nil,sfile) => true
| (:: <pat>,sfile) =>
let val <binding>
val <binding> in (<exp>; <exp>) end)
The body of a function must have the type specified in the function-result type constraint.
The base case of writetofile has type bool while the inductive case has type unit due to TextIO.closeOut(outs); so they don't agree on a return type for the function.
If you would like to return bool in the inductive case, you need something like TextIO.closeOut(outs); true.
By the way, your writeinline function always return unit so you don't need unused false, true values. Here is a corrected version:
fun writetofile([], sfile) = false
| writetofile((l:int)::ls, sfile) =
let
val outs = TextIO.openOut(sfile)
fun writeinline(outs,[]) = TextIO.closeOut(outs)
| writeinline(outs, l::ls)= ( TextIO.output(outs (Int.toString(l)^"\n"));
writeinline(outs, ls) )
in
( writeinline(outs, l::ls);
TextIO.closeOut(outs);
true )
end