Like the if .. then in code
# let rec range2 a b accum =
if b < a then accum
else range2 a (b - 1) (b :: accum);;
how to write this b < a as a pattern in match .. with ? sth like
let rec range2 a b accum = match .. with
| ..
If you don't need to structurally pattern match anything, then match likely doesn't make sense. If/else exists for reason, after all.
However, you may want to learn about conditional guards on patterns. Let's say I wanted to convert an int to a type with constructors Zero, Even and Odd.
let convert =
function
| 0 -> Zero
| n when n % 2 = 0 -> Even
| _ -> Odd
As compared to:
let convert n =
if n = 0 then Zero
else if n % 2 = 0 then Even
else Odd
Or:
let convert =
function
| 0 -> Zero
| n -> if n % 2 = 0 then Even else Odd
I have seen the following use of pattern-matching to replace if-else (matching on () with guards)
match () with
| _ when b < a -> accum
| _ -> range2 a (b - 1) (b :: accum)
But it is very conflictual whether this is ok to write, or very bad style. Arguments against are that this approach is convoluted / doesn’t use the structual pattern aspect of structural pattern matching. Arguments for are that the match syntax is generally much more readable than the if-else syntax (especially if nesting is involved).
I'm trying to create a function which returns a boolean value to see if the string is balanced. But the result is just not what I expect. Can someone point out what I did wrong?
For example:
false = is_balanced")("
false = is_balanced "(a)b)"
true = is_balanced "fo"
true = is_balanced "a(b(c)d)e"
let rec list_car ch = match ch with
| "" -> []
| ch -> (String.get ch 0 ) :: (list_car (String.sub ch 1 ( (String.length ch)-1) ) ) ;;
let is_balanced klm =
let brk = list_car klm in
let rec left = function
| x::xs, 0 ->
if x = ')' then false
else left (xs, 0)
| x::xs, level ->
if x = '(' then left (xs, succ level)
else if x = ')' then left (xs, pred level)
else left (xs, level)
| [], level -> if level = 0 then true else false
in
left (brk, 0) ;;
But I got the following results:
is_balanced "(((";;
- : bool = true
is_balanced "()";;
- : bool = false
Exploding a string into a list of char by using String.sub is inefficient and unnecessary. Your code can be rewritten as
let is_balanced s =
let rec count pos level =
if (* termination condition *) ... then level = 0
else match s.[pos] with
| '(' -> ...
| ')' -> ...
| _ -> ...
in
count 0 0
With this variant, your mistake should disappear by itself.
Indeed one possible root issue with your previous code is that you are matching on both the list of chars and the level and end up mixing the logic of the termination condition with the logic of the level counting.
In particular, should the level at position n influence the effect of characters on the level at the position n+1?
Presented as an alternative, this is a good opportunity to gain experience with folds. They work well in any situation where you are iterating over something one element at a time and need to evaluate that element in conjunction with an initial state to generate a new value.
The state we'll keep track of is the nesting level. It'll start out as 0. Each iteration, if it's less than 0, the nesting is obviously unbalanced. No further elements from the string will balance the parens, so we'll simply propagate this value to the end of the fold, resulting in a false return.
Otherwise we'll increment or decrement the levels as we encounter open and close parens. Or take no action for non-paren characters.
let balanced str =
let f level ch =
if level < 0 then
level
else
match ch with
| '(' -> level + 1
| ')' -> level - 1
| _ -> level
in
String.fold_left f 0 str = 0
# balanced ")(";;
- : bool = false
# balanced "(a)b)";;
- : bool = false
# balanced "fo";;
- : bool = true
# balanced "a(b(c)d)e";;
- : bool = true
Consider how this operates on "(a)b)":
balanced "(a)b)"
String.fold_left f 0 "(a)b)"
String.fold_left f 1 "a)b)"
String.fold_left f 1 ")b)"
String.fold_left f 0 "b)"
String.fold_left f 0 ")"
String.fold_left f -1 ""
-1
-1 = 0
false
If we look at "a(b(c)d)e":
balanced "a(b(c)d)e"
String.fold_left f 0 "a(b(c)d)e"
String.fold_left f 0 "(b(c)d)e"
String.fold_left f 1 "b(c)d)e"
String.fold_left f 1 "(c)d)e"
String.fold_left f 2 "c)d)e"
String.fold_left f 2 ")d)e"
String.fold_left f 1 "d)e"
String.fold_left f 1 ")e"
String.fold_left f 0 "e"
String.fold_left f 0 ""
0
0 = 0
true
The propagation of the negative level value can be seen if we test "())hello()":
balanced "())hello()"
String.fold_left f 0 "())hello()"
String.fold_left f 1 "))hello()"
String.fold_left f 0 ")hello()"
String.fold_left f -1 "hello()"
String.fold_left f -1 "ello()"
String.fold_left f -1 "llo()"
String.fold_left f -1 "lo()"
String.fold_left f -1 "o()"
String.fold_left f -1 "()"
String.fold_left f -1 ")"
String.fold_left f -1 ""
-1
-1 = 0
false
One note about this approach: it does not short circuit. A string that starts with ) will clearly result in false but the entire string will need to be iterated over.
One way around this would be to have our locally scoped f function raise an exception on level being negative, and then to handle this exception by returning false.
exception Unbalanced_parens
let balanced str =
let f level ch =
if level < 0 then
raise Unbalanced_parens
else
match ch with
| '(' -> level + 1
| ')' -> level - 1
| _ -> level
in
match String.fold_left f 0 str with
| 0 -> true
| _ | exception Unbalanced_parens -> false
Now, if we test "())hello()":
balanced "())hello()"
String.fold_left f 0 "())hello()"
String.fold_left f 1 "))hello()"
String.fold_left f 0 ")hello()"
String.fold_left f -1 "hello()"
raise Unbalanced_parens
false
I believe the problem is with this line:
|x::xs,0-> if x =')'then false else left(xs,0)
Here, if x is not ), the character is simply ignored. Hence, if it is instead (, level will not be incremented.
The quick fix is to instead match the character directly:
| ')'::xs, 0 -> false
But I fully agree with #octachron's advice, and that the problem would have been avoided by not mixing list iteration and level counting.
Also, note that you can use when to attach a condition to a branch. For example, if you match on just the list, you could instead do:
| ')'::xs when level = 0 -> false
And you could also do
| ')'::xs when level < 0 -> false
which is unnecessary in this case, but serves to demonstrate a case that cannot be covered by pattern matching on level directly.
When learning to program, it's important not only to learn programming techniques (such as algorithms) and programming languages, but also tools.
You're confronted with a problem: you've written some code that takes some input and returns the wrong input, but you don't understand why. There are two main approaches to this kind of problem:
Break down the code into smaller pieces, and test them separately, until you find one (or more) that doesn't return the outputs you wanted.
Use debugging tools to look at what's happening inside the program.
Here you can test list_car independently and see that it's not the source of the problem. There's not much you can do to break down is_balanced, but you can take out left and make it a top-level function.
let rec left = function
| x::xs, 0 ->
if x = ')' then false
else left (xs, 0)
| x::xs, level ->
if x = '(' then left (xs, succ level)
else if x = ')' then left (xs, pred level)
else left (xs, level)
| [], level -> if level = 0 then true else false
;;
let is_balanced klm =
let brk = list_car klm in
left (brk, 0) ;;
That's not going to help directly, because the problem is inside left and it's all one thing that can't really be broken down into smaller pieces. However, making left a toplevel function allows you to use a simple debugging tool: tracing in the toplevel. (A function doesn't have to be at the top level to debug it, but it does to use the toplevel's simple tracing function.)
# #trace left;;
left is now traced.
# is_balanced "()";;
left <-- (['('; ')'], 0)
left <-- ([')'], 0)
left --> false
left --> false
Now you can see where the code is going wrong: the second call to left is made with the level 0, but it should be 1 since the previous call opened a parenthesis. So the problem is that left (['('; ')'], 0) is passing the wrong level value when it makes its recursive call.
| x::xs, 0 ->
if x = ')' then false
else left (xs, 0)
^
Now that you've located the problem, the correction should be obvious: in the recursive call, you need to remember the number of opening parentheses somehow — that's what level is for. So level here should be 1, not 0.
let rec left = function
| x::xs, 0 ->
if x = ')' then false
else left (xs, 1)
| x::xs, level ->
if x = '(' then left (xs, succ level)
else if x = ')' then left (xs, pred level)
else left (xs, level)
| [], level -> if level = 0 then true else false
;;
let is_balanced klm =
let brk = list_car klm in
left (brk, 0) ;;
(Remember that in the toplevel, after redefining left, you need to redefine is_balanced as well, even if its textual definition hasn't changed: the old definition refers to the old definition of left.)
I think is_balanced is now correct for inputs that consists solely of parentheses. It doesn't properly ignore other characters, but I'll let you debug that on your own. It may help to write the code in a simpler way, taking advantage of the fact that you can match on the first element of the list, on the level, or both. For example, the incorrect case above was actually unnecessary: you could have made the first case
| ')'::xs, 0 -> false
and the second case already handles lists starting with ( correctly: when the first character is (, the logic is the same regardless of the value of the level.
Now I fixed mt code,and it works!Thanks a lot for the tipps
let rec list_car ch = match ch with
| "" -> []
| ch -> (String.get ch 0 ) :: (list_car (String.sub ch 1 ( (String.length ch)-1) ) ) ;;
let is_balanced klm =
let brk = list_car klm in
let rec left = function
|')'::xs,level ->
if level >0 then left(xs,pred level) else false
| '('::xs,level ->left(xs,succ level)
| _::xs, level -> left (xs, level)
|[],level ->if level =0 then true else false
in
left (brk, 0) ;;
Im trying to run an interpreter I made in ocaml and when i to push in a negative value i.e. let e1 = run [PushI -2; PushI 2; LessThan] []. I am getting a syntax error for my parse_int function. I'm trying to write the part of the function that allows for the input of a negative number
type stackVal =
I of int
type command = PushI of int
let rec run (commands : command list) (stack: stackVal list) : stackVal list =
match (commands , stack) with
| (PushI i :: rest, _ ) -> run rest (I i :: stack)
let to_string (s : stackVal) : string =
match s with
| I i -> string_of_int i
let parse_command (s:string) : command =
match take_while is_alpha (String.trim s) with
| ("PushI" , p) -> let Some i = parse_int (String.trim p) in PushI i
let parse_int (s : string) : int option =
match int_of_string s with
| String.get n 0 = '-' -> Some -String.sub n 1 len
| n -> Some n
| exception _ -> None
There is a problem with the pattern-matching of your parse_int function.
match int_of_string s with
| String.get n 0 = '-' -> Some -String.sub n 1 len
| n -> Some n
| exception _ -> None
The first clause here is invalid as "String.get n 0 = '-'" is not an integer constructor. You could write 1 which matches only the integer 1 or _ whitch matches any integer or n which matches any integer and binds it to the name n for the rest of the clause. You can have a look at the manual for more informations.
If you wanted to check if the first char of the string is - pattern matching is not the right tool to do it, simply use an if then else.
However, note that int_of_string works just fine on negative integers, so there is no need to do that part by yourself.
Unrelated, but i noticed that you call the parse_int in the parse_command function. In that case you should define parse_int before parse_command.
I'm trying to understand the following code to declare a function:
let string_of_int = function
| 0 -> "zero"
| 1 -> "one"
| 2 -> "two"
| _ -> "many"
which is the same as
let string_of_int2 x = match x with
|0 -> "zero"
|1 -> "one"
| 2-> "two"
_ -> "many
I understand The second way of declaring the function with is trying to match the input x with several possibilities that it could be. But I don't understand the first way to do it. What does function keyword do?
Also,
what does 'a'..'z' do in the following code?
let is_capital = function
| 'a'..'z' -> false
| 'A'..'Z' -> true
|_ -> failwith "Not a valid letter"
Why can't I have a function like this:
let examplefunc = function
|"string"-> Printf.printf "a string"
|3 -> Printf.print "an integer"
|true-> Printf.printf "a boolean"
|- -> Printf.printf "whatever"
The function keyword is a variant of fun that takes in account that the behavior of the function often directly depends on the value of the argument. For instance, if we start with the following definition of the factorial function:
For a positive integer n, n! is 1 if n = 0, and n * (n-1)! otherwise
then the natural translation to OCaml is
let factorial = function
| 0 (* if n = 0 *) -> 1
| n (* otherwise *) -> n * factorial (n-1)
like you said this strictly equivalent to
let factorial = fun n -> match n with
| 0 (* if n = 0 *) -> 1
| n (* otherwise *) -> n * factorial (n-1)
but when the argument of the function is immediately deconstructed in a pattern matching, it may be more readable to use function directly.
Concerning '0'..'9', those are range pattern that matches all characters (i.e '0'|'1'|'2'|'3'|'4'|..| '9' between the lower and upper bounds (included) of the range (following the ascii ordering of characters)
let is_digit = function '0'..'9' -> true | _ -> false
is_digit '0' (* returns true *);;
is_digit 'a' (* returns false *);;
let rec parity n = if (n = 0) then
print_string "even" else if (n = 1)
print_string "odd" else
parity (n-2);;
In comparison to...
let rec parity n =
match n with
| 0 -> "even"
| 1 -> "odd"
| _ -> parity(n-2);;
I'm still new to this language. The error in the if-then-else is specifically on my print_string statements w/ a syntax error.
Your second if is missing its then.
(The expression after if doesn't need to be parenthesized in OCaml, incidentally.)