# [erlang-questions] List comprehension puzzler

Raimo Niskanen <>
Tue Sep 20 16:57:13 CEST 2016

```On Tue, Sep 20, 2016 at 09:44:30AM -0400,  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