OCaml - If then else - if-statement

I want to do several things inside a if then else.
I read a string and then I check it's lenght. If it's 3 it does X, if it's 4 it does Y.
I tried:
let str = read_line ()
let first_approach () =
if String.lenght str = 3
then let char1 = String.get str 0
let char2 = String.get str 1
let char3 = String.get str 2
else ()
let second_approach () =
if String.lenght str = 3
then let char1 = String.get str 0
let char2 = String.get str 1
let char3 = String.get str 2
let char4 = String.get str 3
else ()
I want to know how to make all things inside then work. Because I get Syntax error.
Thank you in advance.

There are quite a few problems with this code.
At the outermost level of a module (e.g., a source file or REPL input), you can use let var = val to define a global variable. In your code you're using this construct to define str, for example.
However, whenever let appears anywhere else (inside a declaration or expression) it is used to define a local variable, and must be followed by in and the expression in which the local definition is used. Since you don't have in in your definitions of first_approach and second_approach, your code isn't syntactically valid.
Since the definitions inside first_approach and second_approach are local, there will be no effect after you call them. The definitions for char1 and so on, even if syntactically corrected, won't be global. (As #melpomene says, the code doesn't do anything.)
If you have several expressions separated by ; you can use begin / end to group them into one expression after then or else.
let f x =
if x > 5 then
begin
Printf.printf "x is quite large\n";
Printf.printf "adios\n"
end
If a string s is of length 3, there is no character String.get s 3.
# let s = "abc";;
val s : string = "abc"
# String.get s 3;;
Exception: Invalid_argument "index out of bounds".

You can assign multiple variables in a single let using
let (a, b, c) = (1, 2, 3)
Or using your string example:
let (char1, char2, char3, char4_opt) =
if String.lenght str = 3
then (str.[0], str.[1], str.[2], None)
else (str.[0], str.[1], str.[2], Some str.[3])
Note: I used char4_opt with an option type because there is nothing to put in there for length 3 strings.

Related

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 to convert char list to string in OCaml?

I have a char list ['a';'b';'c']
How do I convert this to the string "abc"?
thanks x
You can create a string of a length, equal to the length of the list, and then fold over the list, with a counter and initialize the string with the contents of the list... But, since OCaml 4.02, the string type started to shift in the direction of immutability (and became immutable in 4.06), you should start to treat strings, as an immutable data structure. So, let's try another solution. There is the Buffer module that is use specifically for the string building:
# let buf = Buffer.create 16;;
val buf : Buffer.t = <abstr>
# List.iter (Buffer.add_char buf) ['a'; 'b'; 'c'];;
- : unit = ()
# Buffer.contents buf;;
- : string = "abc"
Or, as a function:
let string_of_chars chars =
let buf = Buffer.create 16 in
List.iter (Buffer.add_char buf) chars;
Buffer.contents buf
let cl2s cl = String.concat "" (List.map (String.make 1) cl)
Since OCaml 4.07, you can use sequences to easily do that.
let l = ['a';'b';'c'] in
let s = String.of_seq (List.to_seq l) in
assert ( s = "abc" )
Commonly used Base library also offers Base.String.of_char_list

Any side effect of using underscore wildcard in let command (i.e., let _ = ... in) in OCaml?

When using OCaml, I almost always use underscore wildcard in let _ = exp, especially when the result of exp is not important, but the computation inside it is. For example:
let _ = print_endline "abc" in
...
let _ = a := !a + 1 in
...
let _ = do_some_thing ... in
So, I just wonder if there is any side effect of extensively using let _ = ... ?
The side effect is annoying bugs to track in your software in the future. The problem with let _ = is that it will silently ignore partial applications you intended to be total. Suppose you write the following:
let f a b = ...
let _ = f 3 4
And that in the future you add an argument to f:
let f a b c = ...
The expression let _ = f 3 4 will still silently compile and your program will not invoke the function, leaving you wondering what is happening. It is much better to always let to () and use ignore when if you need to ignore a non unit result:
let () = ignore (f 3 4)
let () = print_endline "abc"
Using let _ = ... should be considered bad style.
No, there is absolutely no consequence to using let _ = extensively. The compiler does not add a name to the global environment since you didn't give one.
The purpose of let is to bind values to identifiers. If you doing side-effects only it's its better to wrap it in a begin .. end block. In your case:
begin
print_endline "abc";
a := !a + 1;
do_some_thing ();
end

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

What's the difference between "let ()=" and "let _=" ;

let () = Random.self_init();;
let _ = Random.self_init ();;
│- : unit = ()
It seems "let ()" returns nothing ?
Sincerely!
let is the keyword used to define new variables, like in the following construct:
let pattern = expr
For instance
let a = 2
assigns the value 2 to the name a. (Note this is not a way to assign a value to an already existing variable, but this is another topic).
But the pattern to the left of the = sign can be more than just a name. For instance
let (a,b) = (42,"foo")
defines both a and b, to be respectively 42 and "foo".
Of course, the types on both sides must match.
Which is the case here: both sides are of type int * string.
The expressions to the right of the = sign can also be elaborated, for instance
let foo =
let temp = String.make 10 'a' in
temp.[2] <- 'b';
temp
defines foo as the string "aabaaaaaaa". (As a side note, it also ensures that temp is local to this code snippet).
Now, let's use both: on the left, a pattern matching values of type unit, and on the right, an expression of type unit:
let () = Printf.printf "Hello world!\n"
Which explains the let () = construct.
Now, about the let _, one simply needs to know that _ can be used in a pattern as a wildcard: it matches values of any type and does not bind any name. For instance
let (a,_) = (42,"foo")
defines a as 42, and discards the value "foo". _ means "I know there is something here and I explicitly say I will not use it, so I don't name it". Here _ was used to match values of type string, but it can match value of any type, like int * string:
let _ = (42,"foo")
which does not define any variable and is not very useful. Such constructs are useful when the right hand side has side effects, like this:
let _ = Printf.printf "Hello world!\n"
which explains the second part of the question.
Practical purposes
Both are used and it's rather a matter of taste whether to use one or the other.
let () = is slightly safer as it has the compiler check that the right hand side is of type unit.
A value of any other type than unit is often a bug.
let _ = is slightly shorter (I've seen this argument). (Note that with an editor that automatically closes parenthesizes, the number of keystrokes is the same ;-)
I'm not an OCaml expert, although let me share something :)
The let in OCaml can represent two things:
The way you can assign variables;
The way you can declare functions or assign functions to names;
Using examples, you can see clearly how it works:
Assigning variables:
# let ten = 10;;
val ten : int = 10
# let hello_world_string = "Hello World";;
val hello_world_string : string = "Hello World"
Declaring functions:
# let sum a b = a+b;;
val sum : int -> int -> int = <fun>
# sum 2 3;;
- : int = 5
So, answering the question the difference between let ()= and let _= is:
At first example, you are declaring a function that doesn't have name, parameters nor instructions that should output an unit. The second example, you aren't assigning to _, that is OCaml's wildcard, any value.
As we can see below, we can define a function, that will be executed immediatly because we won't be able to call it anymore:
# let () = print_string "Hello";;
Hello
Or assign to OCaml's wildcard a type and value, or a function:
# let _ = 10;;
- : int = 10
# let _ = print_string "Maybe I answered your question :) ";;
Maybe I answered your question :) - : unit = ()