Four is the number of letters in the ...: Difference between revisions
m (added a related task.) |
m (elided the need for a __TOC__) |
||
Line 42: | Line 42: | ||
:* [[Self-referential sequence]] |
:* [[Self-referential sequence]] |
||
:* [[Spelling of ordinal numbers]] |
:* [[Spelling of ordinal numbers]] |
||
<br> |
<br><br> |
||
=={{header|Kotlin}}== |
=={{header|Kotlin}}== |
Revision as of 21:34, 28 September 2017
The Four is ... sequence is based on the counting of the number of letters in the words of the (never─ending) sentence:
Four is the number of letters in the first word of this sentence, two in the second, three in the third, six in the fourth, two in the fifth, seven in the sixth, ···
- Definitions and directives
-
- English is to be used in spelling numbers.
- Letters are defined as the upper─ and lowercase letters in the Latin alphabet (A──►Z and a──►z).
- Commas are not counted, nor are hyphens (dashes or minus signs).
- twenty─three has eleven letters.
- twenty─three is considered one word (which is hyphenated).
- The American version of numbers will be used here in this task (as opposed to the British version).
2,000,000,000 is two billion, not two milliard.
- Task
-
- Write a driver (invoking routine) and a function (subroutine/routine···) that returns the sequence (for any positive integer) of the number of letters in the first N words in the never─ending sentence. For instance, the portion of the never─ending sentence shown above (2nd sentence of this task), the sequence would be:
4 2 3 6 2 7
- Only construct as much as is needed for the never─ending sentence.
- Write a driver (invoking routine) to show the number of letters in the Nth word, as well as showing the Nth word itself.
- After each test case, show the total number of characters (including blanks, commas, and punctuation) of the sentence that was constructed.
- Show all output here.
- Test cases
Display the first 201 numbers in the sequence (and the total number of characters in the sentence). Display the number of letters (and the word itself) of the 1,000th word. Display the number of letters (and the word itself) of the 10,000th word. Display the number of letters (and the word itself) of the 100,000th word. Display the number of letters (and the word itself) of the 1,000,000th word. Display the number of letters (and the word itself) of the 10,000,000th word (optional).
- Related tasks
Kotlin
This pulls in (slightly adjusted) code from related tasks to convert numbers to text or ordinals. <lang scala>// version 1.1.4-3
val names = mapOf(
1 to "one", 2 to "two", 3 to "three", 4 to "four", 5 to "five", 6 to "six", 7 to "seven", 8 to "eight", 9 to "nine", 10 to "ten", 11 to "eleven", 12 to "twelve", 13 to "thirteen", 14 to "fourteen", 15 to "fifteen", 16 to "sixteen", 17 to "seventeen", 18 to "eighteen", 19 to "nineteen", 20 to "twenty", 30 to "thirty", 40 to "forty", 50 to "fifty", 60 to "sixty", 70 to "seventy", 80 to "eighty", 90 to "ninety"
)
val bigNames = mapOf(
1_000L to "thousand", 1_000_000L to "million", 1_000_000_000L to "billion", 1_000_000_000_000L to "trillion", 1_000_000_000_000_000L to "quadrillion", 1_000_000_000_000_000_000L to "quintillion"
)
val irregOrdinals = mapOf(
"one" to "first", "two" to "second", "three" to "third", "five" to "fifth", "eight" to "eighth", "nine" to "ninth", "twelve" to "twelfth"
)
fun String.toOrdinal(): String {
if (this == "zero") return "zeroth" // or alternatively 'zeroeth' val splits = this.split(' ', '-') var last = splits[splits.lastIndex] return if (irregOrdinals.containsKey(last)) this.dropLast(last.length) + irregOrdinals[last]!! else if (last.endsWith("y")) this.dropLast(1) + "ieth" else this + "th"
}
fun numToText(n: Long, uk: Boolean = false): String {
if (n == 0L) return "zero" val neg = n < 0L val maxNeg = n == Long.MIN_VALUE var nn = if (maxNeg) -(n + 1) else if (neg) -n else n val digits3 = IntArray(7) for (i in 0..6) { // split number into groups of 3 digits from the right digits3[i] = (nn % 1000).toInt() nn /= 1000 }
fun threeDigitsToText(number: Int) : String { val sb = StringBuilder() if (number == 0) return "" val hundreds = number / 100 val remainder = number % 100 if (hundreds > 0) { sb.append(names[hundreds], " hundred") if (remainder > 0) sb.append(if (uk) " and " else " ") } if (remainder > 0) { val tens = remainder / 10 val units = remainder % 10 if (tens > 1) { sb.append(names[tens * 10]) if (units > 0) sb.append("-", names[units]) } else sb.append(names[remainder]) } return sb.toString() }
val strings = Array<String>(7) { threeDigitsToText(digits3[it]) } var text = strings[0] var andNeeded = uk && digits3[0] in 1..99 var big = 1000L for (i in 1..6) { if (digits3[i] > 0) { var text2 = strings[i] + " " + bigNames[big] if (text.length > 0) { text2 += if (andNeeded) " and " else " " // no commas inserted in this version andNeeded = false } else andNeeded = uk && digits3[i] in 1..99 text = text2 + text } big *= 1000 } if (maxNeg) text = text.dropLast(5) + "eight" if (neg) text = "minus " + text return text
}
val opening = "Four is the number of letters in the first word of this sentence,".split(' ')
val String.adjustedLength get() = this.replace(",", "").replace("-", "").length // no ',' or '-'
fun getWords(n: Int): List<String> {
val words = mutableListOf<String>() words.addAll(opening) if (n > opening.size) { var k = 2 while (true) { val len = words[k - 1].adjustedLength val text = numToText(len.toLong()) val splits = text.split(' ') words.addAll(splits) words.add("in") words.add("the") val text2 = numToText(k.toLong()).toOrdinal() + "," // add trailing comma val splits2 = text2.split(' ') words.addAll(splits2) if (words.size >= n) break k++ } } return words
}
fun getLengths(n: Int): Pair<List<Int>, Int> {
val words = getWords(n) val lengths = words.take(n).map { it.adjustedLength } val sentenceLength = words.sumBy { it.length } + words.size - 1 // includes hyphens, commas & spaces return Pair(lengths, sentenceLength)
}
fun getLastWord(n: Int): Triple<String, Int, Int> {
val words = getWords(n) val nthWord = words[n - 1] val nthWordLength = nthWord.adjustedLength val sentenceLength = words.sumBy { it.length } + words.size - 1 // includes hyphens, commas & spaces return Triple(nthWord, nthWordLength, sentenceLength)
}
fun main(args: Array<String>) {
var n = 201 println("The lengths of the first $n words are:\n") val (list, sentenceLength) = getLengths(n) for (i in 0 until n) { if (i % 25 == 0) { if (i > 0) println() print("${"%3d".format(i + 1)}: ") } print("%3d".format(list[i])) } println("\n\nLength of sentence = $sentenceLength\n") n = 1_000 do { var (word, wLen, sLen) = getLastWord(n) if (word.endsWith(",")) word = word.dropLast(1) // strip off any trailing comma println("The length of word $n [$word] is $wLen") println("Length of sentence = $sLen\n") n *= 10 } while (n <= 10_000_000)
}</lang>
- Output:
The lengths of the first 201 words are: 1: 4 2 3 6 2 7 2 3 5 4 2 4 8 3 2 3 6 5 2 3 5 3 2 3 6 26: 3 2 3 5 5 2 3 5 3 2 3 7 5 2 3 6 4 2 3 5 4 2 3 5 3 51: 2 3 8 4 2 3 7 5 2 3 10 5 2 3 10 3 2 3 9 5 2 3 9 3 2 76: 3 11 4 2 3 10 3 2 3 10 5 2 3 9 4 2 3 11 5 2 3 12 3 2 3 101: 11 5 2 3 12 3 2 3 11 5 2 3 11 3 2 3 13 5 2 3 12 4 2 3 11 126: 4 2 3 9 3 2 3 11 5 2 3 12 4 2 3 11 5 2 3 12 3 2 3 11 5 151: 2 3 11 5 2 3 13 4 2 3 12 3 2 3 11 5 2 3 8 3 2 3 10 4 2 176: 3 11 3 2 3 10 5 2 3 11 4 2 3 10 4 2 3 10 3 2 3 12 5 2 3 201: 11 Length of sentence = 1203 The length of word 1000 [in] is 2 Length of sentence = 6279 The length of word 10000 [in] is 2 Length of sentence = 64140 The length of word 100000 [one] is 3 Length of sentence = 659474 The length of word 1000000 [the] is 3 Length of sentence = 7113621 The length of word 10000000 [thousand] is 8 Length of sentence = 70995756
Perl 6
Uses the Lingua::EN::Numbers::Cardinal module to generate both cardinal and ordinal numbers. This module places commas in number words between 3-orders-of-magnitude clusters. E.G. 12345678.&ordinal
becomes: twelve million, three hundred forty-five thousand, six hundred seventy-eighth. Uses a custom 'no-commas' routine to filter them out for accurate character counts. Generates the 'sentence' lazily so only the words needed are ever calculated and reified.
<lang perl6>use Lingua::EN::Numbers::Cardinal;
my $index = 1; my @sentence = flat 'Four is the number of letters in the first word of this sentence, '.words,
{ @sentence[$index++].&alpha.&cardinal, 'in', 'the', |($index.&ordinal.&no-commas~',').words } ... * ;
sub alpha ( $str ) { $str.subst(/\W/, , :g).chars } sub no-commas ( $str ) { $str.subst(',', , :g) }
say 'First 201 word lengths in the sequence:'; put ' ', map { @sentence[$_].&alpha.fmt("%2d") ~ (((1+$_) %% 25) ?? "\n" !! ) }, ^201; say 'Characters in the sentence: ', @sentence[^201].join(' ').chars, ", up to and including this word.\n" ;
for 1e3, 1e4, 1e5, 1e6, 1e7 {
say "{$_.&ordinal.tc} word, '{@sentence[$_ - 1]}', has {@sentence[$_ - 1].&alpha} characters: ", "Sentence is {@sentence[^$_].join(' ').chars} characters long up to and including this word.\n";
}</lang>
- Output:
First 201 word lengths in the sequence: 4 2 3 6 2 7 2 3 5 4 2 4 8 3 2 3 6 5 2 3 5 3 2 3 6 3 2 3 5 5 2 3 5 3 2 3 7 5 2 3 6 4 2 3 5 4 2 3 5 3 2 3 8 4 2 3 7 5 2 3 10 5 2 3 10 3 2 3 9 5 2 3 9 3 2 3 11 4 2 3 10 3 2 3 10 5 2 3 9 4 2 3 11 5 2 3 12 3 2 3 11 5 2 3 12 3 2 3 11 5 2 3 11 3 2 3 13 5 2 3 12 4 2 3 11 4 2 3 9 3 2 3 11 5 2 3 12 4 2 3 11 5 2 3 12 3 2 3 11 5 2 3 11 5 2 3 13 4 2 3 12 3 2 3 11 5 2 3 8 3 2 3 10 4 2 3 11 3 2 3 10 5 2 3 11 4 2 3 10 4 2 3 10 3 2 3 12 5 2 3 11 Characters in the sentence: 1203, up to and including this word. One thousandth word, 'in', has 2 characters: Sentence is 6249 characters long up to and including this word. Ten thousandth word, 'in', has 2 characters: Sentence is 64097 characters long up to and including this word. One hundred thousandth word, 'one', has 3 characters: Sentence is 659455 characters long up to and including this word. One millionth word, 'the', has 3 characters: Sentence is 7113560 characters long up to and including this word. Ten millionth word, 'thousand', has 8 characters: Sentence is 70995729 characters long up to and including this word.
REXX
<lang rexx>/*REXX pgm finds/shows the number of letters in the Nth word in a constructed sentence*/ @= 'Four is the number of letters in the first word of this sentence,' /*···*/
/* [↑] the start of a long sentence. */
parse arg N M /*obtain optional argument from the CL.*/ if N= | N="," then N= 201 /*Not specified? Then use the default.*/ if M= | M="," then M=1000 10000 100000 1000000 /* " " " " " " */ @abcU= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' /*define the uppercase Latin alphabet. */ !.=.; #.=.; q=1; w=length(N) /* [↓] define some helpful low values.*/ call tell N if N<0 then say y ' is the length of word ' a " ["word(@, a)"]" say /* [↑] N negative? Just show 1 number*/ say 'length of sentence= ' length(@) /*display the length of the @ sentence.*/
if M\== then do k=1 for words(M) while M\=0 /*maybe handle counts (if specified). */
x=word(M, k) /*obtain the Kth word of the M list. */ call tell -x /*invoke subroutine (with negative arg)*/ say say y ' is the length of word ' x " ["word(@, x)"]" say 'length of sentence= ' length(@) /*display length of @ sentence.*/ end /*k*/
exit /*stick a fork in it, we're all done. */ /*──────────────────────────────────────────────────────────────────────────────────────*/ wordLen: arg ?; return length(?) - length( space( translate(?, , @abcU), 0) ) /*──────────────────────────────────────────────────────────────────────────────────────*/ tell: parse arg z,,$; idx=1; a=abs(z); group=25 /*show 25 numbers per line.*/
/*Q is the last number spelt by $SPELL#*/ do j=1 for a /*traipse through all the numbers to N.*/ do 2 /*perform loop twice (well ··· maybe).*/ y=wordLen( word(@, j) ) /*get the Jth word from the sentence.*/ if y\==0 then leave /*Is the word spelt? Then we're done.*/ q=q + 1 /*bump the on─going (moving) # counter.*/ if #.q==. then #.q=$spell#(q 'Q ORD') /*need to spell A as an ordinal number?*/ _=wordLen( word(@, q) ) /*use the length of the ordinal number.*/ if !._==. then !._=$spell#(_ 'Q') /*Not spelled? Then go and spell it. */ @=@ !._ 'in the' #.q"," /*append words to never─ending sentence*/ end /*2*/ /* [↑] Q ≡ Quiet ORD ≡ ORDinal */
$=$ || right(y, 3) /* [↓] append a justified # to a line.*/ if j//group==0 & z>0 then do; say right(idx, w)'►'$; idx=idx+group; $=; end end /*j*/ /* [↑] show line if there's enough #s.*/
if $\== & z>0 then say right(idx, w)'►'$ /*display if there are residual numbers*/ return</lang>
The $SPELL#.REX routine can be found here ───► $SPELL#.REX.
- output:
1► 4 2 3 6 2 7 2 3 5 4 2 4 8 3 2 3 6 5 2 3 5 3 2 3 6 26► 3 2 3 5 5 2 3 5 3 2 3 7 5 2 3 6 4 2 3 5 4 2 3 5 3 51► 2 3 8 4 2 3 7 5 2 3 10 5 2 3 10 3 2 3 9 5 2 3 9 3 2 76► 3 11 4 2 3 10 3 2 3 10 5 2 3 9 4 2 3 11 5 2 3 12 3 2 3 101► 11 5 2 3 12 3 2 3 11 5 2 3 11 3 2 3 13 5 2 3 12 4 2 3 11 126► 4 2 3 9 3 2 3 11 5 2 3 12 4 2 3 11 5 2 3 12 3 2 3 11 5 151► 2 3 11 5 2 3 13 4 2 3 12 3 2 3 11 5 2 3 8 3 2 3 10 4 2 176► 3 11 3 2 3 10 5 2 3 11 4 2 3 10 4 2 3 10 3 2 3 12 5 2 3 201► 11 length of sentence= 1203 2 is the length of word 1000 [in] length of sentence= 6279 2 is the length of word 10000 [in] length of sentence= 64140 3 is the length of word 100000 [one] length of sentence= 659474 3 is the length of word 1000000 [the] length of sentence= 7113621
zkl
Uses the nth function from Spelling_of_ordinal_numbers#zkl <lang zkl> // Built the sentence in little chucks but only save the last one
// Save the word counts
fcn fourIsThe(text,numWords){
const rmc="-,"; seq:=(text - rmc).split().apply("len").copy(); // (4,2,3,6...) szs:=Data(numWords + 100,Int).howza(0).extend(seq); // bytes cnt,lastWords := seq.len(),""; total:=seed.len() - 1; // don't count trailing space
foreach idx in ([1..]){ sz:=szs[idx]; a,b := nth(sz,False),nth(idx+1); // "two","three hundred sixty-seventh" lastWords="%s in the %s, ".fmt(a,b); ws:=lastWords.counts(" ")[1]; // "five in the forty-ninth " --> 4 cnt+=ws; total+=lastWords.len(); lastWords.split().pump(szs.append,'-(rmc),"len"); if(cnt>=numWords){
if(cnt>numWords){ z,n:=lastWords.len(),z-2; do(cnt - numWords){ n=lastWords.rfind(" ",n) - 1; } lastWords=lastWords[0,n+1]; total-=(z - n); } break;
} } return(lastWords.strip(),szs,total);
} fcn lastWord(sentence){ sentence[sentence.rfind(" ")+1,*] }</lang> <lang zkl>var seed="Four is the number of letters in the first word of this sentence, "; sentence,szs,total := fourIsThe(seed,201); print(" 1:"); foreach n,x in ([1..201].zip(szs)){
print("%3d".fmt(x)); if(0 == n%25) print("\n%3d:".fmt(n+1));
} println("\nLength of above sentence: ",total);</lang>
- Output:
1: 4 2 3 6 2 7 2 3 5 4 2 4 8 3 2 3 6 5 2 3 5 3 2 3 6 26: 3 2 3 5 5 2 3 5 3 2 3 7 5 2 3 6 4 2 3 5 4 2 3 5 3 51: 2 3 8 4 2 3 7 5 2 3 10 5 2 3 10 3 2 3 9 5 2 3 9 3 2 76: 3 11 4 2 3 10 3 2 3 10 5 2 3 9 4 2 3 11 5 2 3 12 3 2 3 101: 11 5 2 3 12 3 2 3 11 5 2 3 11 3 2 3 13 5 2 3 12 4 2 3 11 126: 4 2 3 9 3 2 3 11 5 2 3 12 4 2 3 11 5 2 3 12 3 2 3 11 5 151: 2 3 11 5 2 3 13 4 2 3 12 3 2 3 11 5 2 3 8 3 2 3 10 4 2 176: 3 11 3 2 3 10 5 2 3 11 4 2 3 10 4 2 3 10 3 2 3 12 5 2 3 201: 11 Length of above sentence: 1203
<lang zkl>n:=1000; do(5){
sentence,x,total := fourIsThe(seed,n); word:=lastWord(sentence); println("%,d words: \"%s\" [%d]. Length=%,d"
.fmt(n,word,word.len(),total));
n*=10;
}</lang>
- Output:
1,000 words: "in" [2]. Length=6,247 10,000 words: "in" [2]. Length=64,095 100,000 words: "one" [3]. Length=659,453 1,000,000 words: "the" [3]. Length=7,140,558 10,000,000 words: "thousand" [8]. Length=71,250,727