Ccyan
Ccyan

Reputation: 1057

Erlang sumif function

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.

Upvotes: 1

Views: 100

Answers (3)

BlackMamba
BlackMamba

Reputation: 10254

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.

Upvotes: 0

Hynek -Pichi- Vychodil
Hynek -Pichi- Vychodil

Reputation: 26121

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]).

Upvotes: 2

Dogbert
Dogbert

Reputation: 222188

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

Upvotes: 4

Related Questions