[erlang-questions] List comprehension puzzler

Raimo Niskanen raimo+erlang-questions@REDACTED
Tue Sep 20 16:57:13 CEST 2016


On Tue, Sep 20, 2016 at 09:44:30AM -0400, lloyd@REDACTED wrote:
> 'Lo again Hernando,
> 
> Shortly after I responded to your post I realized that you might be interested in the problem that led to my list comprehension puzzler question:
> 
> I'm working on a web application that requires users to enter International Standard Book Numbers (ISBNs). Thus I need a function to validate the format of user input.
> 
> ISBNS are either 10 or 13-digits. The standard specifies a check-sum algorithm, but that seemed too complicated for my purposes. So, after struggling with the list comprehension puzzler and Dan Gudmundsson's helpful input, I came up with this:
> 
> isbn_format_valid(ISBN) ->
>    D = fun(I) -> (I >= $0) and (I =< 9) end,
>    NotIntegers = [I || I <- ISBN, D(I) == true],
>    Flag1 = NotIntegers == [],
>    Flag2 = (length(ISBN) == 13) or (length(ISBN) == 10),
>    Flag1 and Flag2.
> 
> As you can see, our minds ran down the same track.
> 
> There may be a faster, more elegant, way to solve the problem, but this seems to do the job. Now if I can only figure out how to use the Library of Congress API to retrieve book data...

Small improvement, finish with:
    .... == true],
    L = length(ISBN),
    (NotIntegers == []) andalso ((L == 10) orelse (L == 13)).

And, your fun() D would accept this: [1.0, 2.0, 3.0, ...] so write it as:
    D = fun(I) -> is_integer(I) andalso ($0 =< I) andalso (I =< $9) end,

Your list comprehension can be replaced with:
    NotIntegers = lists:filter(D, ISBN),

Resulting in (eliminating NotIntegers with a case statement):

isbn_format_valid(ISBN) ->
    D = fun(I) -> is_integer(I) andalso ($0 =< I) andalso (I =< $9) end,
    case lists:filter(D, ISBN) of
	[] ->
	    L = length(ISBN),
	    (L == 10) orelse (L == 13);
	_ ->
	    false
    end.

Note that that will construct a result list with all non-integers just to
check that it is empty, so a slimmer, faster solution would be to do it in
plain function recursion:

isbn_format_valid(ISBN) ->
    isbn_format_valid(ISBN, 0).
%%
isbn_format_valid([_|_], N) when N > 13 -> false
isbn_format_valid([I|Is], N)
    if
	is_integer(I), $0 =< I, I =< $9 ->
	    isbn_format_valid(Is, N+1);
	true ->
	    false
    end;
isbn_format_valid([], 10) ->
    true;
isbn_format_valid([], 13) ->
    true;
isbn_format_valid([], _) ->
    false.



And here is a (God Forbid) regexp solution:

isbn_format_valid(ISBN) ->
    case re:run(ISBN, "^(\\d{10}|\\d{13})$", [{capture,none}] of
	match -> true;
	nomatch -> false
    end.

-- 

/ Raimo Niskanen, Erlang/OTP, Ericsson AB



More information about the erlang-questions mailing list