[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