Dries Vandenberk
Dries Vandenberk

Reputation: 65

Erlang code split String without delimiter and put into list

I am very new to programming in Erlang. I am making a program to decode braille but I am having a problem trying to split a string every 2 characters with the absence of a delimiter, and putting them into a list.

First I read in the braille alphabet like this:

inKey(Key1) -> Key1.
inKey2(Key2) -> Key2.
inKey3(Key3) -> Key3.

Key 1-3 are strings and look like this:"x.x.xxxxx.xxxxx..x.xx.x.xxxxx.xxxxx..x.xx.x..xxxxxx." these 3 keys form the braille information that I will later use to transform braille into normal characters.

Now I need to split this string and place them in a list so that it would look like this: ["x.","x.","xx","xx",x.","xx","xx","x.",".x and so on.

If the string is split I want to insert them into my coding list like shown in Tuplet for the character A

 Code=[#row{name="R1",alfabet=[#codes{letter="A",braille="X."},#codes{letter="B",braille=""}

Can someone helpe me out?

`

Upvotes: 3

Views: 989

Answers (2)

Pascal
Pascal

Reputation: 14042

It is late to post this, nevertheless I do it because I think that the type of variable you want to use to store the alphabet is not really appropriate to the different usages you may have. I wrote a small code to show the usage of two maps (one for decoding, one for encoding). Sorry there are only a few comments in the code.

-module (braille).

-export ([test/1,show_maps/0]).

test(Str) ->
    % build the conversion map
    {Alphabet,Braille} = init(),
    % transform a string into braille and print it
    Lines = print_braille(Str,Alphabet),
    % transform the braille result into string , check the result and print it
    Str = read_braille(Lines,Braille),
    ok.

show_maps() ->
    {Alphabet,Braille} =init(),
    io:format("Alphabet = ~n~100p~n~nBraille = ~n~100p~n",[Alphabet,Braille]).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% creates one map to allow the conversion from latin letter to braille: Alphabet
%     and one map to allow the conversion from braille to latin : Braille
% This should be the init function of a conversion server, in charge to maintain the conversion maps
init() ->
    L1="x.x.xxxxx.xxxxx..x.xx.x.xxxxx.xxxxx..x.xx.x..xxxxxx.",
    L2="..x....x.xx.xxxxx.xx..x....x.xx.xxxxx.xx..x.xx...x.x",
    L3="....................x.x.x.x.x.x.x.x.x.x.xxxx.xxxxxxx",

    LL1 = split_str(L1),
    LL2 = split_str(L2),
    LL3 = split_str(L3),

    Alphabet = init_alphabet(LL1,LL2,LL3,$a,#{$ => #{1 => "..", 2 => "..", 3=> ".."}}),
    Braille = init_braille(Alphabet),
    {Alphabet,Braille}.

% a tail recursive function to split the input string into packets of 2 characters
split_str_tail_recursive([],R) ->
    lists:reverse(R);
split_str_tail_recursive([A,B|Rest],R) ->
    split_str_tail_recursive(Rest,[[A,B]|R]).
% interface to call the recursive fuction
split_str(L) -> split_str_tail_recursive(L,[]).

init_alphabet([],[],[],_,R) ->
    R;
init_alphabet([H1|T1],[H2|T2],[H3|T3],C,R) ->
    init_alphabet(T1,T2,T3,C+1,maps:put(C,#{1 => H1, 2 => H2, 3=> H3},R)).

init_braille(Alphabet) ->
    H = fun(K,V,AccIn) -> maps:put({maps:get(1,V),maps:get(2,V),maps:get(3,V)},K,AccIn) end,
    maps:fold(H,#{},Alphabet).

%transform a latin lower cap string into 3 lines representing a braille output, print them and returns the 3 lines
print_braille(S,Alphabet) ->
    Line1 = lists:flatmap(fun(C) -> maps:get(1,maps:get(C,Alphabet)) end,S),
    Line2 = lists:flatmap(fun(C) -> maps:get(2,maps:get(C,Alphabet)) end,S),
    Line3 = lists:flatmap(fun(C) -> maps:get(3,maps:get(C,Alphabet)) end,S),
    io:format("~s~n~s~n~s~n",[Line1,Line2,Line3]),
    {Line1,Line2,Line3}.

% transform a tuple of 3 lines braille representation into latin string, print it and return it
read_braille({Line1,Line2,Line3},Braille) ->
    List = lists:zip3(split_str(Line1),split_str(Line2),split_str(Line3)),
    Str = lists:map(fun(B) -> maps:get(B,Braille) end,List),
    io:format("~s~n", [Str]),
    Str.

and here the usage:

1> c(braille).
{ok,braille}
2> braille:show_maps().
Alphabet = 
#{32 => #{1 => "..",2 => "..",3 => ".."},
  97 => #{1 => "x.",2 => "..",3 => ".."},
  98 => #{1 => "x.",2 => "x.",3 => ".."},
  99 => #{1 => "xx",2 => "..",3 => ".."},
  100 => #{1 => "xx",2 => ".x",3 => ".."},
  101 => #{1 => "x.",2 => ".x",3 => ".."},
  102 => #{1 => "xx",2 => "x.",3 => ".."},
  103 => #{1 => "xx",2 => "xx",3 => ".."},
  104 => #{1 => "x.",2 => "xx",3 => ".."},
  105 => #{1 => ".x",2 => "x.",3 => ".."},
  106 => #{1 => ".x",2 => "xx",3 => ".."},
  107 => #{1 => "x.",2 => "..",3 => "x."},
  108 => #{1 => "x.",2 => "x.",3 => "x."},
  109 => #{1 => "xx",2 => "..",3 => "x."},
  110 => #{1 => "xx",2 => ".x",3 => "x."},
  111 => #{1 => "x.",2 => ".x",3 => "x."},
  112 => #{1 => "xx",2 => "x.",3 => "x."},
  113 => #{1 => "xx",2 => "xx",3 => "x."},
  114 => #{1 => "x.",2 => "xx",3 => "x."},
  115 => #{1 => ".x",2 => "x.",3 => "x."},
  116 => #{1 => ".x",2 => "xx",3 => "x."},
  117 => #{1 => "x.",2 => "..",3 => "xx"},
  118 => #{1 => "x.",2 => "x.",3 => "xx"},
  119 => #{1 => ".x",2 => "xx",3 => ".x"},
  120 => #{1 => "xx",2 => "..",3 => "xx"},
  121 => #{1 => "xx",2 => ".x",3 => "xx"},
  122 => #{1 => "x.",2 => ".x",3 => "xx"}}

Braille = 
#{{"..","..",".."} => 32,
  {".x","x.",".."} => 105,
  {".x","x.","x."} => 115,
  {".x","xx",".."} => 106,
  {".x","xx",".x"} => 119,
  {".x","xx","x."} => 116,
  {"x.","..",".."} => 97,
  {"x.","..","x."} => 107,
  {"x.","..","xx"} => 117,
  {"x.",".x",".."} => 101,
  {"x.",".x","x."} => 111,
  {"x.",".x","xx"} => 122,
  {"x.","x.",".."} => 98,
  {"x.","x.","x."} => 108,
  {"x.","x.","xx"} => 118,
  {"x.","xx",".."} => 104,
  {"x.","xx","x."} => 114,
  {"xx","..",".."} => 99,
  {"xx","..","x."} => 109,
  {"xx","..","xx"} => 120,
  {"xx",".x",".."} => 100,
  {"xx",".x","x."} => 110,
  {"xx",".x","xx"} => 121,
  {"xx","x.",".."} => 102,
  {"xx","x.","x."} => 112,
  {"xx","xx",".."} => 103,
  {"xx","xx","x."} => 113}
ok
3> braille:test("the quick brown fox jumps over the lazy dog").
.xx.x...xxx..xxxx...x.x.x..xxx..xxx.xx...xx.xxxx.x..x.x.x.x....xx.x...x.x.x.xx..xxx.xx
xxxx.x..xx..x.......x.xx.xxx.x..x..x....xx....x.x....xx..xxx..xxxx.x..x....x.x...x.xxx
x.......x.xx....x.....x.x..xx.....x.xx....xxx.x.x...x.xx..x...x.......x...xxxx....x...
the quick brown fox jumps over the lazy dog
ok
4>

Upvotes: 2

7stud
7stud

Reputation: 48599

In Erlang, you need to remember that a string is equivalent to a list of numbers, where the numbers are the ascii codes for each character. The confusing thing is that sometimes the shell displays a list of numbers as a string, and sometimes it displays a list of numbers as a list of numbers. That is a TERRIBLE feature of the erlang shell.

Despite what the shell displays, just remember that a string is a list of numbers. The problem then becomes, what if you want to output a list of numbers and not a string? The answer is: you can't do anything about that; the shell may display your list of numbers as a string...unless you take further action:

45> Grades = [97,98,99].
"abc"

Wtf??!

46> io:format("~w~n", [Grades]).
[97,98,99]
ok

Another way of thinking about it is this: the erlang string syntax "abc" is just a shortcut for creating the list of numbers [97,98,99].

Next, you can deconstruct a list with a pattern like this:

[Head|Tail] = [1,2,3]

In the shell:

8> [Head|Tail] = [1, 2, 3, 4].
[1,2,3,4]

9> Head.                      
1

10> Tail.
[2,3,4]

But, the cons operator | is more flexible than that, and it allows you to do this:

13> [N1,N2 | T] = [1, 2, 3, 4].   
[1,2,3,4]

14> N1.
1

15> N2.
2

16> T.
[3,4]

Therefore, you can do this:

-module(my).
-compile(export_all).

string() ->
    "x.x.xxxxx.xxxxx..x.xx.x.xxxxx.xxxxx..x.xx.x..xxxxxx.".

chunk2([]) -> [];
chunk2([N1, N2| Tail]) ->
    [[N1,N2] | chunk2(Tail) ].

In the shell:

2> c(my).
my.erl:2: Warning: export_all flag enabled - all functions will be exported
{ok,my}

3> my:chunk2(my:string()).  
["x.","x.","xx","xx","x.","xx","xx","x.",".x",".x","x.",
 "x.","xx","xx","x.","xx","xx","x.",".x",".x","x.","x.",".x",
 "xx","xx","x."]

4> 

Finally, to construct a list of #code{} records, you can do this:

-module(my).
-compile(export_all).
-record(codes, {letter, braille}).

string() ->
    "x.x.xxxxx.xxxxx..x.xx.x.xxxxx.xxxxx..x.xx.x..xxxxxx.".

chunk2([]) -> [];
chunk2([N1, N2| Tail]) ->
    [[N1,N2] | chunk2(Tail) ].

make_record_list(Letters, Chunks) ->
    lists:zipwith(
        fun(Letter, Chunk) -> #codes{letter=[Letter], braille=Chunk} end,
        Letters,
        Chunks
    ).

In the shell:

31> c(my).             
my.erl:2: Warning: export_all flag enabled - all functions will be exported
{ok,my}

32> ListOfCapLetters = lists:seq($A, $Z).
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"

33> BrailleChunks = my:chunk2(my:string()).
["x.","x.","xx","xx","x.","xx","xx","x.",".x",".x","x.",
 "x.","xx","xx","x.","xx","xx","x.",".x",".x","x.","x.",".x",
 "xx","xx","x."]

34> Records = my:make_record_list(ListOfCapLetters, BrailleChunks).
[{codes,"A","x."},
 {codes,"B","x."},
 {codes,"C","xx"},
 {codes,"D","xx"},
 {codes,"E","x."},
 {codes,"F","xx"},
 {codes,"G","xx"},
 {codes,"H","x."},
 {codes,"I",".x"},
 {codes,"J",".x"},
 {codes,"K","x."},
 {codes,"L","x."},
 {codes,"M","xx"},
 {codes,"N","xx"},
 {codes,"O","x."},
 {codes,"P","xx"},
 {codes,"Q","xx"},
 {codes,"R","x."},
 {codes,"S",".x"},
 {codes,"T",".x"},
 {codes,"U","x."},
 {codes,"V","x."},
 {codes,"W",".x"},
 {codes,"X","xx"},
 {codes,"Y","xx"},
 {codes,"Z",[...]}]

It looks like there may be a problem with the last record, so let's check:

37> tl(Records).  
[{codes,"B","x."},
 {codes,"C","xx"},
 {codes,"D","xx"},
 {codes,"E","x."},
 {codes,"F","xx"},
 {codes,"G","xx"},
 {codes,"H","x."},
 {codes,"I",".x"},
 {codes,"J",".x"},
 {codes,"K","x."},
 {codes,"L","x."},
 {codes,"M","xx"},
 {codes,"N","xx"},
 {codes,"O","x."},
 {codes,"P","xx"},
 {codes,"Q","xx"},
 {codes,"R","x."},
 {codes,"S",".x"},
 {codes,"T",".x"},
 {codes,"U","x."},
 {codes,"V","x."},
 {codes,"W",".x"},
 {codes,"X","xx"},
 {codes,"Y","xx"},
 {codes,"Z","x."}]

Nope, the first output just reached the limit of what the shell was willing to display.

Note that each element of ListOfCapLetters is a number, and a number is not a list, so each element of ListOfCapLetters is not a string itself. To create a string from a number, you need to put it inside a list, hence [Letter]. It's the same difference between: String = [97,98,99] and ListOfStrings = [[97], [98], [99]]:

40> String = [97,98,99].
"abc"

41> hd(String).
97

42> ListOfStrings = [[97], [98], [99]].
["a","b","c"]

43> hd(ListOfStrings).
"a"

lists:seq() returns the equivalent of String.

Response to comment:

See lists:keyfind/3:

20> TargetRecord = lists:keyfind("xx", 3, Records).                
#codes{letter = "C",braille = "xx"}

21> rr(my).  %Read record definition contained in my.erl into the shell.                                                     
[codes]

22> TargetRecord#codes.letter.
"C"

The string "C" is actually the list [67], which the shell has decided to display as "C". You need to be able to look up an ascii table to discover what a string really represents in Erlang. Or, you can get the ascii code of a character like this:

24> $C.
67

25> $a.
97

So, "C" is actually the list [67], and "a" is actually the list [97].

Upvotes: 6

Related Questions