Skip to content

Commit a2d01a7

Browse files
committed
working
1 parent 4379c10 commit a2d01a7

File tree

2 files changed

+204
-3
lines changed

2 files changed

+204
-3
lines changed

integer-to-english/README.org

+102-3
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ Input: num = 1234567
2323
Output: "One Million Two Hundred Thirty Four Thousand Five Hundred Sixty Seven"
2424

2525
* Brainstorming
26+
What's the max here? That detemrmines how many decimal place value names I have to hard code src_emacs-lisp[]{(expt 2 31)} {{{results(=2147483648=)}}}. Ok, so that's just "billions", easy.
27+
2628
Let's see if we can just say the rules in english and I'll try to avoid saying that the right answer is just to throw it at ChatGPT.
2729

2830
I'm thinking about examining the number form the back forward
@@ -103,7 +105,7 @@ Clearly I'm going to need to do pattern matching, lets see how it looks in ruby
103105

104106
* Implementation
105107
:PROPERTIES:
106-
:header-args+: :noweb yes
108+
:header-args+: :noweb strip-export :exports both
107109
:header-args:ruby+: :ruby "/opt/homebrew/opt/ruby/bin/ruby"
108110
:END:
109111

@@ -157,6 +159,8 @@ Now lets try to do two digits
157159
""
158160
in ["0", d]
159161
digit_to_english d
162+
in ["1", "0"]
163+
"ten"
160164
in ["1", "1"]
161165
"eleven"
162166
in ["1", "2"]
@@ -173,6 +177,8 @@ Now lets try to do two digits
173177
"thirty #{digit_to_english d}"
174178
in ["5", d]
175179
"fifty #{digit_to_english d}"
180+
in ["8", d]
181+
"eighty #{digit_to_english d}"
176182
in [d1, d2]
177183
"#{digit_to_english d1}ty #{digit_to_english d2}"
178184
end
@@ -209,8 +215,10 @@ Ok, so its becoming clear that it might be useful for ~dte~ to be able to handle
209215
case digits
210216
in x if x.length <= 2
211217
two_digits_to_english x
218+
in ["0", *d23]
219+
two_digits_to_english d23
212220
in [d1, *d23]
213-
"#{two_digits_to_english [d1]} hundred #{two_digits_to_english d23}"
221+
"#{two_digits_to_english [d1]} hundred #{two_digits_to_english d23}".strip
214222
end
215223
end
216224
#+end_src
@@ -236,4 +244,95 @@ now this can be simplified to the following. Here we alias our new ~three_digits
236244
- for a 9 digit number its ~(dte d123) million (6dte d456789)~ - we'll alias this to 9dte
237245
- for a 10 digit number its ~(dte d1) billion (9dte d234567890)~
238246

239-
So now, we just know the breaks and teh word associated to each of the breaks and then we do something like ~(dte head..break) word rest~
247+
So now, we just know the breaks and the word associated to each of the breaks and then we do something like ~(dte head..break) word rest~
248+
249+
Ok so lets do that. There's the question of what the structure for those breaks/word associations should look like. While we could do an array or a hash, because we're always processing it from highest break to lowest I think the best approach is more like a linked list as it can be unrolled more easily. Quick google tells me Ruby has one-line structs that can be used for this. Note that we have special handling for "hundreds and below" already so no need to go lower
250+
251+
#+name: dynamic-place-name
252+
#+begin_src ruby :results silent :session
253+
PlaceName = Struct.new(:place, :name, :next)
254+
ALL_PLACE_NAMES = PlaceName.new(10, "billion",
255+
PlaceName.new(7, "million",
256+
PlaceName.new(4, "thousand")))
257+
#+end_src
258+
259+
Note the capitalization here is interesting. I got stuck on it for a bit. In ruby - unlike other languages - all caps matters for making your variable visible down the scope chain
260+
261+
We're almost there, we can now unroll this across all our digits
262+
263+
There's one gocha here, in that if the next set of digits are all 0, then we don't want to say anything. This will allow us to handle situations like =10000= recursively without saying the "hundred" that you *would* say if you've got a number like =100= or =10100=
264+
265+
#+name: many-digits-to-english
266+
#+begin_src ruby :results silent :session
267+
def many_digits_to_english(digits, place_name)
268+
if digits.all? { |d| d == "0" } # the hundreds in 1000
269+
""
270+
elsif not place_name # terminal condition and when 3 digit or lower
271+
three_digits_to_english digits
272+
elsif digits.length < place_name.place # when not in the billions and need to get down to the place name that matters
273+
many_digits_to_english(digits, place_name.next)
274+
else
275+
split_at = digits.length - place_name.place
276+
place_digits = digits[0..split_at]
277+
rest_digits = digits[(split_at + 1)..-1]
278+
if place_digits.all? { |d| d == "0" } # situations like 1000001
279+
many_digits_to_english(rest_digits, place_name.next)
280+
else # normal case
281+
"#{three_digits_to_english place_digits} #{place_name.name} #{many_digits_to_english(rest_digits, place_name.next)}"
282+
end
283+
end
284+
end
285+
#+end_src
286+
Now we just have to do the splitting of digits. Oh, and handle zero
287+
288+
#+name: number-to-english
289+
#+begin_src ruby :results silent :session
290+
def number_to_english(num)
291+
if num == 0
292+
"zero"
293+
else
294+
many_digits_to_english(num.to_s.split(""), ALL_PLACE_NAMES).strip
295+
end
296+
end
297+
#+end_src
298+
299+
And here's all of it
300+
301+
#+name: all-together-now
302+
#+begin_src ruby :results silent :session
303+
<<digit-to-english>>
304+
305+
<<two-digits-to-english>>
306+
307+
<<three-digits-to-english>>
308+
309+
<<dynamic-place-name>>
310+
311+
<<many-digits-to-english>>
312+
313+
<<number-to-english>>
314+
#+end_src
315+
316+
lets test it out. Note that
317+
318+
#+begin_src ruby :results output :tangle number_to_english.rb
319+
<<all-together-now>>
320+
321+
[0, 4, 12, 99, 100, 911, 1000, 10001, 100000, 1000001, 1000000000, 2000000011].each { |n| puts "#{n}, #{(number_to_english n)}" }
322+
#+end_src
323+
324+
#+RESULTS:
325+
#+begin_example
326+
0, zero
327+
4, four
328+
12, twelve
329+
99, ninety nine
330+
100, one hundred
331+
911, nine hundred eleven
332+
1000, one thousand
333+
10001, ten thousand one
334+
100000, one hundred thousand
335+
1000001, one million one
336+
1000000000, one billion
337+
2000000011, two billion eleven
338+
#+end_example
+102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
def digit_to_english(digit)
2+
case digit
3+
in "0"
4+
""
5+
in "1"
6+
"one"
7+
in "2"
8+
"two"
9+
in "3"
10+
"three"
11+
in "4"
12+
"four"
13+
in "5"
14+
"five"
15+
in "6"
16+
"six"
17+
in "7"
18+
"seven"
19+
in "8"
20+
"eight"
21+
in "9"
22+
"nine"
23+
end
24+
end
25+
26+
def two_digits_to_english(digits)
27+
case digits
28+
in [d]
29+
digit_to_english d
30+
in ["0", "0"]
31+
""
32+
in ["0", d]
33+
digit_to_english d
34+
in ["1", "0"]
35+
"ten"
36+
in ["1", "1"]
37+
"eleven"
38+
in ["1", "2"]
39+
"twelve"
40+
in ["1", "3"]
41+
"thirteen"
42+
in ["1", "5"]
43+
"fifteen"
44+
in ["1", d]
45+
"#{digit_to_english d}teen"
46+
in ["2", d]
47+
"twenty #{digit_to_english d}"
48+
in ["3", d]
49+
"thirty #{digit_to_english d}"
50+
in ["5", d]
51+
"fifty #{digit_to_english d}"
52+
in ["8", d]
53+
"eighty #{digit_to_english d}"
54+
in [d1, d2]
55+
"#{digit_to_english d1}ty #{digit_to_english d2}"
56+
end
57+
end
58+
59+
def three_digits_to_english(digits)
60+
case digits
61+
in x if x.length <= 2
62+
two_digits_to_english x
63+
in ["0", *d23]
64+
two_digits_to_english d23
65+
in [d1, *d23]
66+
"#{two_digits_to_english [d1]} hundred #{two_digits_to_english d23}".strip
67+
end
68+
end
69+
70+
PlaceName = Struct.new(:place, :name, :next)
71+
ALL_PLACE_NAMES = PlaceName.new(10, "billion",
72+
PlaceName.new(7, "million",
73+
PlaceName.new(4, "thousand")))
74+
75+
def many_digits_to_english(digits, place_name)
76+
if digits.all? { |d| d == "0" } # the hundreds in 1000
77+
""
78+
elsif not place_name # terminal condition and when 3 digit or lower
79+
three_digits_to_english digits
80+
elsif digits.length < place_name.place # when not in the billions and need to get down to the place name that matters
81+
many_digits_to_english(digits, place_name.next)
82+
else
83+
split_at = digits.length - place_name.place
84+
place_digits = digits[0..split_at]
85+
rest_digits = digits[(split_at + 1)..-1]
86+
if place_digits.all? { |d| d == "0" } # situations like 1000001
87+
many_digits_to_english(rest_digits, place_name.next)
88+
else # normal case
89+
"#{three_digits_to_english place_digits} #{place_name.name} #{many_digits_to_english(rest_digits, place_name.next)}"
90+
end
91+
end
92+
end
93+
94+
def number_to_english(num)
95+
if num == 0
96+
"zero"
97+
else
98+
many_digits_to_english(num.to_s.split(""), ALL_PLACE_NAMES).strip
99+
end
100+
end
101+
102+
[0, 4, 12, 99, 100, 911, 1000, 10001, 100000, 1000001, 1000000000, 2000000011].each { |n| puts "#{n}, #{(number_to_english n)}" }

0 commit comments

Comments
 (0)