21

The code is below:

-module(map_demo).
-export([count_characters/1]).

count_characters(Str) ->
count_characters(Str, #{}).

count_characters([H|T], #{ H => N } = X) ->
    count_characters(T, X#{ H := N+1 });
count_characters([H|T], X) ->
    count_characters(T, X#{ H => 1});
count_characters([], X) ->
    X.

when compiling the code in the Erlang shell, it reported the following errors:

1> c(map_demo).
map_demo.erl:7: illegal pattern
map_demo.erl:8: variable 'N' is unbound
map_demo.erl:10: illegal use of variable 'H' in map
map_demo.erl:7: Warning: variable 'H' is unused
error

I'm new in Erlang, and just can't find anything wrong by myself. How to correct it?

geeker
  • 345
  • 6
  • 15
  • 2
    The closest thing I can find around is that this is a "known bug". Are these erlang people serious? This is such a huge bug in that case! I think this is something you took from "Programming Erlang" by Joe Armstrong himself, and I am shocked to the very core that this doesn't work! – Navneeth G Nov 28 '14 at 06:47
  • As of Erlang/OTP 18.0 this code throws error "only" for line 7, 8 because bound variables are legal to use in maps: ________________________________________________________________________________ 1> `K = thecake, #{K => isalie}.` _______________________________________________ `#{thecake => isalie}` – toraritte Jul 08 '15 at 01:20
  • running version 20, and this is STILL a bug?! Maybe I will research a bit more before getting angry, but... https://stackoverflow.com/questions/44247735/map-pattern-matching-in-erlang-unexpected-error-unbound – alexakarpov May 29 '17 at 17:47
  • @alexakarpov No, the bug (point 1 in the accepted answer) was fixed in R18 and the problem that remains (point 2) isn't a bug. Or rather, it's a bug in the book, not in Erlang. – Alexey Romanov May 29 '17 at 19:29

7 Answers7

18

The answers from IRC (#erlang@freenode):

  1. variables as keys in matches are not supported yet (release 17.0)
  2. A more general issue affects matching arguments of a function: line 7's H is matched 2 times; or once and used to match N then. (This issue also appears with binaries)

This should be solved in the coming releases.

As of release 17 this works:

-module(count_chars).
-export([count_characters/1]).

count_characters(Str) ->
        count_characters(Str, #{}).

%% maps module functions cannot be used as guards (release 17)
%% or you'll get "illegal guard expression" error
count_characters([H|T], X) ->
    case maps:is_key(H,X) of
        false -> count_characters(T, maps:put(H,1,X));
        true  -> Count = maps:get(H,X),
                         count_characters(T, maps:update(H,Count+1,X))
    end;
count_characters([], X) ->
        X.

Here is another version (only tested on 18) that is slightly more similar to the one in the book:

-module(count_chars).
-export([count_characters/1]).

count_characters(Str) ->
        count_characters(Str, #{}).

count_characters([H|T], X) ->
    case maps:is_key(H,X) of
        false -> count_characters(T, X#{ H => 1 });
        true  -> #{ H := Count } = X,
                 count_characters(T, X#{ H := Count+1 })
    end;
count_characters([], X) ->
        X.
Community
  • 1
  • 1
fenollp
  • 2,406
  • 1
  • 15
  • 14
  • 1
    Since (1) is the case, what is the correct way to write this code using maps in 17.0? – JDong Jan 01 '15 at 22:10
3

Quoted from OTP 17.0 Release Notes:

OTP-11616 == erts stdlib hipe dialyzer compiler typer ==

    EEP43: New data type - Maps

    With Maps you may for instance:

    -- M0 = #{ a => 1, b => 2}, % create associations

    -- M1 = M0#{ a := 10 }, % update values

    -- M2 = M1#{ "hi" => "hello"}, % add new associations

    -- #{ "hi" := V1, a := V2, b := V3} = M2. % match keys with
    values

    For information on how to use Maps please see the Reference
    Manual.

    The current implementation is without the following features:

    -- No variable keys

    -- No single value access

    -- No map comprehensions

    Note that Maps is experimental during OTP 17.0.

Currently you can use maps module to implement count_characters:

count_characters(Str) ->
    count_characters(Str, #{}).

count_characters([H|T], X) -> 
    count_characters(T, maps:put(H, maps:get(H, X, 0) + 1, X));
count_characters([], X) ->
    X.
an0
  • 17,191
  • 12
  • 86
  • 136
2

When you want to match a map, you need like this:

#{key1 := Pattern1, key2 := Pattern2, ...} = VarContainingAMap.

you can read the document: https://joearms.github.io/published/2014-02-01-big-changes-to-erlang.html

BlackMamba
  • 10,054
  • 7
  • 44
  • 67
1

@EWit, Felipe Mafra:

maps does just what it is supposed to do; what's missing here is the reduce part:

count(Str) -> M = count_chars(Str, maps:new()), % maps part, bad naming
    L = maps:to_list(M),                        % to be able to sum
    N = [X || {_,X} <- L],                      % strip the numbers
    lists:sum(N).                               % sum them up

count_chars([H|T], Map) when is_map(Map)->
    N = maps:get(H, Map, 0),
    count_chars(T, maps:put(H, N + 1, Map));
count_chars([], Map) -> Map.
kklepper
  • 763
  • 8
  • 13
-1

Problem in match syntax.

Fof match use :=. Example

test(#{ key := Test }) -> Test.

And for associated key and value use =>. Example: M = #{ keynew => 123 }

saa
  • 245
  • 5
  • 10
-1

I guess you are using R17 since this feature is available only from this version.

looking at some doc, my understanding is that you should write the code that way (I can't test it, I am still using R15 :o)

-module(map_demo).
-export([count_characters/1]).

count_characters(Str) ->
count_characters(Str, #{}).

count_characters([H|T], #{ H := N } = X) ->
    count_characters(T, X#{ H := N+1 });
count_characters([H|T], X) ->
    count_characters(T, X#{ H => 1});
count_characters([], X) ->
    X.
Pascal
  • 13,977
  • 2
  • 24
  • 32
-2

-module(count_chars).

%% API

-export([count/1]).

count(Str) -> count_chars(Str, maps:new()).

count_chars([H|T], Map) when is_map(Map)->

N = maps:get(H, Map, 0),
count_chars(T, maps:put(H, N + 1, Map));

count_chars([], Map) -> Map.

Community
  • 1
  • 1
  • Please elaborate on what was changed to make this answer function properly. Just giving code does not assist in clarifying what was wrong in the first place. – EWit Dec 18 '14 at 22:23