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?
`
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].
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>
How to convert numbers to words in ruby?
I know there is a gem somewhere. Trying to implement it without a gem. I just need the numbers to words in English for integers. Found this but it is very messy. If you have any idea on how to implement a cleaner easier to read solution please share.
http://raveendran.wordpress.com/2009/05/29/ruby-convert-number-to-english-word/
Here is what I have been working on. But having some problem implementing the scales. The code is still a mess. I hope to make it more readable when it functions properly.
class Numberswords
def in_words(n)
words_hash = {0=>"zero",1=>"one",2=>"two",3=>"three",4=>"four",5=>"five",6=>"six",7=>"seven",8=>"eight",9=>"nine",
10=>"ten",11=>"eleven",12=>"twelve",13=>"thirteen",14=>"fourteen",15=>"fifteen",16=>"sixteen",
17=>"seventeen", 18=>"eighteen",19=>"nineteen",
20=>"twenty",30=>"thirty",40=>"forty",50=>"fifty",60=>"sixty",70=>"seventy",80=>"eighty",90=>"ninety"}
scale = [000=>"",1000=>"thousand",1000000=>" million",1000000000=>" billion",1000000000000=>" trillion", 1000000000000000=>" quadrillion"]
if words_hash.has_key?(n)
words_hash[n]
#still working on this middle part. Anything above 999 will not work
elsif n>= 1000
print n.to_s.scan(/.{1,3}/) do |number|
print number
end
#print value = n.to_s.reverse.scan(/.{1,3}/).inject([]) { |first_part,second_part| first_part << (second_part == "000" ? "" : second_part.reverse.to_i.in_words) }
#(value.each_with_index.map { |first_part,second_part| first_part == "" ? "" : first_part + scale[second_part] }-[""]).reverse.join(" ")
elsif n <= 99
return [words_hash[n - n%10],words_hash[n%10]].join(" ")
else
words_hash.merge!({ 100=>"hundred" })
([(n%100 < 20 ? n%100 : n.to_s[2].to_i), n.to_s[1].to_i*10, 100, n.to_s[0].to_i]-[0]-[10])
.reverse.map { |num| words_hash[num] }.join(" ")
end
end
end
#test code
test = Numberswords.new
print test.in_words(200)
My take on this
def in_words(int)
numbers_to_name = {
1000000 => "million",
1000 => "thousand",
100 => "hundred",
90 => "ninety",
80 => "eighty",
70 => "seventy",
60 => "sixty",
50 => "fifty",
40 => "forty",
30 => "thirty",
20 => "twenty",
19=>"nineteen",
18=>"eighteen",
17=>"seventeen",
16=>"sixteen",
15=>"fifteen",
14=>"fourteen",
13=>"thirteen",
12=>"twelve",
11 => "eleven",
10 => "ten",
9 => "nine",
8 => "eight",
7 => "seven",
6 => "six",
5 => "five",
4 => "four",
3 => "three",
2 => "two",
1 => "one"
}
str = ""
numbers_to_name.each do |num, name|
if int == 0
return str
elsif int.to_s.length == 1 && int/num > 0
return str + "#{name}"
elsif int < 100 && int/num > 0
return str + "#{name}" if int%num == 0
return str + "#{name} " + in_words(int%num)
elsif int/num > 0
return str + in_words(int/num) + " #{name} " + in_words(int%num)
end
end
end
puts in_words(4) == "four"
puts in_words(27) == "twenty seven"
puts in_words(102) == "one hundred two"
puts in_words(38_079) == "thirty eight thousand seventy nine"
puts in_words(82102713) == "eighty two million one hundred two thousand seven hundred thirteen"
Have you considered humanize ?
https://github.com/radar/humanize
Simple answer use humanize gem and you will get desired output
Install it directly
gem install humanize
Or add it to your Gemfile
gem 'humanize'
And you can use it
require 'humanize'
1.humanize #=> 'one'
345.humanize #=> 'three hundred and forty-five'
1723323.humanize #=> 'one million, seven hundred and twenty-three thousand, three hundred and twenty-three'
If you are using this in rails you can directly use this
NOTE: As mentioned by sren in the comments below. The humanize method provided by ActiveSupport is different than the gem humanize
You can also use the to_words gem.
This Gem converts integers into words.
e.g.
1.to_words # one ,
100.to_words # one hundred ,
101.to_words # one hundred and one
It also converts negative numbers.
I can see what you're looking for, and you may wish to check out this StackOverflow post: Number to English Word Conversion Rails
Here it is in summary:
No, you have to write a function yourself. The closest thing to what
you want is number_to_human, but that does not convert 1 to One.
Here are some URLs that may be helpful:
http://codesnippets.joyent.com/posts/show/447
http://raveendran.wordpress.com/2009/05/29/ruby-convert-number-to-english-word/
http://deveiate.org/projects/Linguistics/
I am not quite sure, if this works for you. Method can be called like this.
n2w(33123) {|i| puts i unless i.to_s.empty?}
Here is the method ( I have not tested it fully. I think it works upto million. Code is ugly, there is a lot of room for re-factoring. )
def n2w(n)
words_hash = {0=>"zero",1=>"one",2=>"two",3=>"three",4=>"four",5=>"five",6=>"six",7=>"seven",8=>"eight",9=>"nine",
10=>"ten",11=>"eleven",12=>"twelve",13=>"thirteen",14=>"fourteen",15=>"fifteen",16=>"sixteen",
17=>"seventeen", 18=>"eighteen",19=>"nineteen",
20=>"twenty",30=>"thirty",40=>"forty",50=>"fifty",60=>"sixty",70=>"seventy",80=>"eighty",90=>"ninety"}
scale = {3=>"hundred",4 =>"thousand",6=>"million",9=>"billion"}
if words_hash.has_key?n
yield words_hash[n]
else
ns = n.to_s.split(//)
while ns.size > 0
if ns.size == 2
yield("and")
yield words_hash[(ns.join.to_i) - (ns.join.to_i)%10]
ns.shift
end
if ns.size > 4
yield(words_hash[(ns[0,2].join.to_i) - (ns[0,2].join.to_i) % 10])
else
yield(words_hash[ns[0].to_i])
end
yield(scale[ns.size])
ns.shift
end
end
end
def subhundred number
ones = %w{zero one two three four five six seven eight nine
ten eleven twelve thirteen fourteen fifteen
sixteen seventeen eighteen nineteen}
tens = %w{zero ten twenty thirty **forty** fifty sixty seventy eighty ninety}
subhundred = number % 100
return [ones[subhundred]] if subhundred < 20
return [tens[subhundred / 10]] if subhundred % 10 == 0
return [tens[subhundred / 10], ones[subhundred % 10]]
end
def subthousand number
hundreds = (number % 1000) / 100
tens = number % 100
s = []
s = subhundred(hundreds) + ["hundred"] unless hundreds == 0
s = s + ["and"] unless hundreds == 0 or tens == 0
s = s + [subhundred(tens)] unless tens == 0
end
def decimals number
return [] unless number.to_s['.']
digits = number.to_s.split('.')[1].split('').reverse
digits = digits.drop_while {|d| d.to_i == 0} . reverse
digits = digits.map {|d| subhundred d.to_i} . flatten
digits.empty? ? [] : ["and cents"] + digits
end
def words_from_numbers number
steps = [""] + %w{thousand million billion trillion quadrillion quintillion sextillion}
result = []
n = number.to_i
steps.each do |step|
x = n % 1000
unit = (step == "") ? [] : [step]
result = subthousand(x) + unit + result unless x == 0
n = n / 1000
end
result = ["zero"] if result.empty?
result = result + decimals(number)
result.join(' ').strip
end
def words_from_numbers(number)
ApplicationHelper.words_from_numbers(number)
end
Its been quite a while since the question was asked. Rails has something inbuilt for this now.
https://api.rubyonrails.org/classes/ActionView/Helpers/NumberHelper.html
number_to_human(1234567) # => "1.23 Million"
number_to_human(1234567890) # => "1.23 Billion"
number_to_human(1234567890123) # => "1.23 Trillion"
number_to_human(1234567890123456) # => "1.23 Quadrillion"
number_to_human(1234567890123456789) # => "1230 Quadrillion"