Coming from a Lisp background, Erlang's case statement seems a bit baffling to me. I'm currently working on the Sieve of Eratosthenes problem, trying to generate a list of the prime factors of a number N. However I can't seem to migrate what seems like a simple cond statement in Lisp to the equivalent in Erlang.
So far I have constructed an if statement that resembles how I would go about things in Lisp:
prime_factors(N) -> pf_helper(N, [], 2).
pf_helper(M, PL, D) ->
if
prime_p(M) == true -> [ M | PL ];
(prime_p(D) == true) and (M rem D == 0) -> pl_helper(M div D, [ D | PL ], D+1);
(prime_p(D) == true) and (M rem D /= 0) -> pl_helper(M, PL, D+1);
prime_p(D) == false -> pl_helper(M, PL, D+1)
end.
I am aware that this will not compile since I can only have BIF calls in my guards. The problem is that I cannot conceptualize how this would run as a case statement, since case handles a conditional expression on one argument. In the case that I represent the three pf_helper arguments in a tuple:
pf_helper(M,PL,D) -> pf_helper({M, PL, D}).
what would the patterns be inside the case statements that correspond to the conditional expressions
prime_p(M) == true
(prime_p(D) == true) and (M rem D == 0)
(prime_p(D) == true) and (M rem D /= 0)
prime_p(D) == false
?
Use some guards:
case {prime_p(M), prime_p(D)} of
{true, _} -> [M|PL];
{false, true} when (M rem D == 0) -> ...;
{false, true} when (M rem D /= 0) -> ...;
{false, false} -> ...
end
I think Anthony Ramine was working on implementing cond at one point.
Related
I don't understand erlang very well and for the past number of hours I've been searching up ways to fix this but I can't find any good If statement examples.
I'm trying to use a recursive function to move discs from one hanoi tower to another. However I get the "no true branch found when evaluating an if expression". I understand as far as needing true -> somewhere?
-module(hanoi).
-export([main/1]).
%% Create Towers
main(NumDiscs) ->
TowerA = create_tower(NumDiscs),
TowerB = [],
TowerC = [],
display_towers(TowerA,TowerB,TowerC),
solve(NumDiscs,TowerA,TowerB,TowerC).
%% Add the number of discs inputted to the first tower
create_tower(0) -> [];
create_tower(NumDiscs) when NumDiscs > 0 ->
[NumDiscs] ++ create_tower(NumDiscs - 1).
%% Display all towers
display_towers(A,B,C) ->
io:format("-------------------------\n"),
io:format("Tower A: ~p\n",[A]),
io:format("Tower B: ~p\n",[B]),
io:format("Tower C: ~p\n",[C]).
solve(Disc,TowA,TowB,TowC) ->
if Disc > 0 ->
solve(Disc - 1,TowA,TowB,TowC),
TowBNew = [lists:last(TowA)],
TowCNew = [lists:append(TowC,TowBNew)],
TowANew = [lists:sublist(TowA,length(TowA) - 1)],
display_towers(TowANew,TowB,TowCNew),
solve(Disc - 1,TowANew,TowCNew,TowB)
end.
An if expression in Erlang must have a branch that evaluates to true. I'm guessing that error is thrown when Disc > 0 is false. You need to add a true -> clause to that:
solve(Disc,TowA,TowB,TowC) ->
if Disc > 0 ->
solve(Disc - 1,TowA,TowB,TowC),
TowBNew = [lists:last(TowA)],
TowCNew = [lists:append(TowC,TowBNew)],
TowANew = [lists:sublist(TowA,length(TowA) - 1)],
display_towers(TowANew,TowB,TowCNew),
solve(Disc - 1,TowANew,TowCNew,TowB); % <- note the ';'
true ->
% do something here
end.
Here is my code from a long time ago. Hope this can help you:
main(N)->
towerhanoi(N, a, b, c).
towerhanoi(N, A, C, B)->
case N of
1 ->
change(N, A, C);
_ ->
towerhanoi(N-1, A, B, C),
change(N, A, C),
towerhanoi(N-1 , B, C, A)
end.
change(N, A, C)->
io:format("change disc ~p from tower ~p to tower ~p~n", [N, A, C]).
I am building a function that counts of many times a character appears in a string after the nth position.
countCh ("aaabbbccc", 3, 'b')
val it: int = 2
In C, I would use an accumulator with a while loop. But I am trying to learn the F# functional face, where this approach is discouraged.
So I used guards to test few conditions and build the function:
let rec countCh (s:string, n:int, ch:char) =
match s, n, ch with
| (s, n, ch) when n > s.Length -> 0 //p1
| (s, n, ch) when n < 0 -> 0 //p2
| (s, n, ch) when s.[n] <> ch -> countCh(s, n + 1, ch) //p3
| (s, n, ch) when s.[n] = ch -> 1 + countCh(s, n + 1, ch) //p4
The coexistence of patterns 3 and 4 is problematic (impossible, I am afraid). Even if it compiles, I have not been able to make it work. How can this task functionally be handled?
First, the coexistence of these branches is not problematic. They don't conflict with each other. Why do you think that it's problematic? Is it because you get an "Incomplete pattern match" compiler warning? That warning does not tell you that the branches conflict, it tells you that the compiler can't prove that the four branches cover all possibilities. Or do you think that for some other reason? If you want your questions to be answered accurately, you'll have to ask them more clearly.
Second, you're abusing the pattern matching. Look: there are no patterns! The patterns in every branch are exactly the same, and trivial. Only guards are different. This looks very counterintuitively within a match, but would be plainly expressed with if..elif:
let rec countCh (s:string) n ch =
if n >= s.Length || n < 0 then 0
elif s.[n] = ch then 1 + countCh s (n + 1) ch
else countCh s (n + 1) ch
NOTE 1: see how I made the parameters curried? Always use curried form, unless there is a very strong reason to use tupled. Curried parameters are much more convenient to use on the caller side.
NOTE 2: your condition n > s.Length was incorrect: string indices go from 0 to s.Length-1, so the bail condition should be n >= s.Length. It is corrected in my code.
Finally, since this is an exercise, I must point out that the recursion is not tail recursion. Look at the second branch (in my code): it calls the function recursively and then adds one to the result. Since you have to do something with the result of the recursive call, the recursion can't be "tail". This means you risk stack overflow on very long inputs.
To make this into tail recursion, you need to turn the function "inside out", so to say. Instead of returning the result from every call, you need to pass it into every call (aka "accumulator"), and only return from the terminal case:
let rec countCh (s:string) n ch countSoFar =
if n >= s.Length || n < 0 then countSoFar
elif s.[n] = ch then countCh s (n+1) ch (countSoFar+1)
else countCh s (n+1) ch countSoFar
// Usage:
countCh "aaaabbbccc" 5 'b' 0
This way, every recursive call is the "last" call (i.e. the function doesn't do anything with the result, but passes it straight out to its own caller). This is called "tail recursion" and can be compiled to work in constant stack space (as opposed to linear).
I agree with the other answers, but I'd like to help you with your original question. You need to indent the function, and you have an off by one bug:
let rec countCh (s:string, n:int, ch:char) =
match s, n, ch with
| s, n, _ when n >= s.Length-1 -> 0 //p1
| s, _, _ when n < 0 -> 0 //p2
| s, n, ch when s.[n+1] <> ch -> countCh(s, n+2, ch) //p3
| s, n, ch when s.[n+1] = ch -> 1 + countCh(s, n+2, ch) //p4
I'd suggest to not write it yourself, but ask the library functions for help:
let countCh (s: string, n, c) =
s.Substring(n+1).ToCharArray()
|> Seq.filter ((=) c)
|> Seq.length
Or use Seq.skip, along with the fact that you can drop the conversion to character array:
let countCh (s: string, n, c) =
s
|> Seq.skip (n + 1)
|> Seq.filter ((=) c)
|> Seq.length
I'm trying to make a sumif function in Erlang that would return a sum of all elements in a list if the predicate function evaluates to true. Here is what I have:
sumif(_, []) -> undefined;
sumif(Fun, [H|T]) -> case Fun(H) of
true -> H + sumif(Fun, T);
false -> sumif(Fun, T)
end.
I also implemented my own pos function which returns true if a number is greater than 0 and false otherwise:
pos(A) -> A > 0.
I tried using pos with sumif but I'm getting this error:
exception error: bad function pos
Why is this happening? Is it because of my sumif function or pos? I have tested pos on its own and it seems to work just fine.
Edit: It might be because how I'm calling the function. This is how I'm currently calling it: hi:sumif(pos,[-1,1,2,-3]). Where hi is my module name.
Is it because of my sumif function or pos?
It's because of sumif. You should return 0 when an empty list is passed, as it'll be called from the 2nd clause when T is []:
-module(a).
-compile(export_all).
sumif(_, []) -> 0;
sumif(Fun, [H|T]) -> case Fun(H) of
true -> H + sumif(Fun, T);
false -> sumif(Fun, T)
end.
pos(A) -> A > 0.
Test:
1> c(a).
{ok,a}
2> a:sumif(fun a:pos/1, [-4, -2, 0, 2, 4]).
6
List comprehensions make things far simpler:
sumif(F, L) ->
lists:sum([X || X <- L, F(X)]).
Dobert's answer is of cousrse right, problem is your sum for empty list.
If your concern is performance a little bit you should stick to tail recursive solution (in this case it matter because there is not lists:reverse/1 involved).
sumif(F, L) ->
sumif(F, L, 0).
sumif(F, [], Acc) when is_function(F, 1) -> Acc;
sumif(F, [H|T], Acc) ->
New = case F(H) of
true -> H+Acc;
false -> Acc
end,
sumif(F, T, New).
Ways how to make correct function for first parameter:
F1 = fun pos/1, % inside module where pos/1 defined
F2 = fun xyz:pos/1, % exported function from module xyz (hot code swap works)
N = 0,
F3 = fun(X) -> X > N end, % closure
% test it
true = lists:all(fun(F) -> is_function(F, 1) end, [F1, F2, F3]).
There has tow error in your code:
1. sumif(_, []) -> undefined; should return 0, not undefined.
2. when you pass pos(A) -> A > 0. to sumif/2,you should use fun pos/1, please read http://erlang.org/doc/programming_examples/funs.html#id59138
sumif(F, L) ->
lists:foldl(fun(X, Sum) when F(X) -> Sum+X; (_) -> Sum end, 0, L).
You can use lists:foldl.
The following is my solution to Project Euler 14, which works (in 18 s):
%Which starting number, under one million, produces the longest Collartz chain?
-module(soln14).
-export([solve/0]).
collatz(L) ->
[H|T] = L,
F = erlang:get({'collatz', H}),
case is_list(F) of
true ->
R = lists:append(F, T);
false ->
if H == 1 ->
R = L;
true ->
if H rem 2 == 0 ->
R = collatz([H div 2 | L]);
true ->
R = collatz([3*H+1 | L])
end
end,
erlang:put({'collatz', lists:last(L)}, R),
R
end.
dosolve(N, Max, MaxN, TheList) ->
if N == 1000000 -> MaxN;
true ->
L = collatz([N]),
M = length(L),
if M > Max -> dosolve(N+1, M, N, L);
true ->
dosolve(N+1, Max, MaxN, TheList)
end
end.
solve() ->
{Megass, Ss, Micros} = erlang:timestamp(),
S = dosolve(1, -1, 1, []),
{Megase, Se, Microe} = erlang:timestamp(),
{Megase-Megass, Se-Ss, Microe-Micros, S}.
However, the compiler complains:
8> c(soln14).
soln14.erl:20: Warning: variable 'R' is unused
{ok,soln14}
9> soln14:solve().
{0,18,-386776,837799}
Is this a compiler scoping error, or do I have a legit bug?
It's not a compiler error, just a warning that in the true case of "case is_list(F) of", the bindning of R to the result of lists:append() is pointless, since this value of R will not be used after that point, just returned immediately. I'll leave it to you to figure out if that's a bug or not. It may be that you are fooled by your indentation. The lines "erlang:put(...)," and "R" are both still within the "false" case of "case is_list(F) of", and should be deeper indented to reflect this.
The error message and the code are not "synchronized". with the version you give, the warning is on line 10: R = lists:append(F, T);.
What it means is that you bind the result of the lists:append/2 call to R and that you don't use it later in the true statement.
this is not the case in the false statement since you use R in the function erlang:put/2.
You could write the code this way:
%Which starting number, under one million, produces the longest Collartz chain?
-module(soln14).
-export([solve/0,dosolve/4]).
collatz(L) ->
[H|T] = L,
F = erlang:get({'collatz', H}),
case is_list(F) of
true ->
lists:append(F, T);
false ->
R = if H == 1 ->
L;
true ->
if H rem 2 == 0 ->
collatz([H div 2 | L]);
true ->
collatz([3*H+1 | L])
end
end,
erlang:put({'collatz', lists:last(L)}, R),
R
end.
dosolve(N, Max, MaxN, TheList) ->
if N == 1000000 -> MaxN;
true ->
L = collatz([N]),
M = length(L),
if M > Max -> dosolve(N+1, M, N, L);
true ->
dosolve(N+1, Max, MaxN, TheList)
end
end.
solve() ->
timer:tc(?MODULE,dosolve,[1, -1, 1, []]).
Warning the code uses a huge amount of memory, collatz is not tail recursive, and it seems that there is some garbage collecting witch is not done.
I know recursive function is a powerful technique in F#. My question is: Is there an exit statement, which can jump out recursive functions, just like imperative languages. For example, Insert a node to a binary tree.
type Tree<'a> when 'a :> IComparable<'a> =
| Nil
| Leaf of 'a
| Node of Tree<'a> * 'a * Tree<'a>
let tt2 = Node(
Node(Leaf "D", "B",Node(Leaf "G", "E", Leaf "H" )),
"A",
Node(Nil, "C", Node(Nil, "F", Leaf "I")))
let rec contains (x : #IComparable<'a>) = function
| Nil -> false
| Leaf y -> if x.CompareTo(y) = 0 then true else false
| Node(l, y, r) ->
match l, y, r with
| l, y, Nil -> if x.CompareTo(y) = 0 then true else contains x l
| Nil,y, r -> if x.CompareTo(y) = 0 then true else contains x r
| _ -> if x.CompareTo(y) = 0 then true
else contains x r |>ignore
contains x l
let xx = contains "C" tt2 //It is wrong answer.
Is there an exit statement, which can jump out recursive functions, just like imperative languages.
No. The very reason is that you can encode imperative break/return by recursive functions and pattern matching. If you would like to break, just return a value, otherwise invoke another recursive call.
This question is more appropriate to ask for high-order functions. When you need early exit on high-order functions, writing custom recursive function is the way to go. If you are interested in imperative constructs in F#, take a look at the excellent series by #Tomas.
Your function will exit at some branch when the condition is determined. The only problem is that you should not discard contain x r in the second to last line.
You can remove superfluous if/else for clarity
let rec contains (x : #IComparable<'a>) = function
| Nil -> false
| Leaf y -> x.CompareTo(y) = 0
| Node(l, y, r) ->
match l, y, r with
| l, y, Nil -> x.CompareTo(y) = 0 || contains x l
| Nil,y, r -> x.CompareTo(y) = 0 || contains x r
| _ -> x.CompareTo(y) = 0 || contains x l || contains x r