Spelling of ordinal numbers: Difference between revisions
(Add Go solution.) |
|||
Line 39: | Line 39: | ||
* [[N'th]] |
* [[N'th]] |
||
<br><br> |
<br><br> |
||
=={{header|Go}}== |
|||
As with the Kotlin solution, this uses the output of `say` from the |
|||
[[Number_names#Go|Number_names]] task. |
|||
<lang Go>import ( |
|||
"fmt" |
|||
"strings" |
|||
) |
|||
func main() { |
|||
for _, n := range []int64{ |
|||
1, 2, 3, 4, 5, 11, 65, 100, 101, 272, 23456, 8007006005004003, |
|||
} { |
|||
fmt.Println(sayOrdinal(n)) |
|||
} |
|||
} |
|||
var irregularOrdinals = map[string]string{ |
|||
"one": "first", |
|||
"two": "second", |
|||
"three": "third", |
|||
"five": "fifth", |
|||
"eight": "eighth", |
|||
"nine": "ninth", |
|||
"twelve": "twelfth", |
|||
} |
|||
func sayOrdinal(n int64) string { |
|||
s := say(n) |
|||
i := strings.LastIndexAny(s, " -") |
|||
i++ |
|||
// Now s[:i] is everything upto and including the space or hyphen |
|||
// and s[i:] is the last word; we modify s[i:] as required. |
|||
// Since LastIndex returns -1 if there was no space/hyphen, |
|||
// `i` will be zero and this will still be fine. |
|||
if x, ok := irregularOrdinals[s[i:]]; ok { |
|||
s = s[:i] + x |
|||
} else if s[len(s)-1] == 'y' { |
|||
s = s[:i] + s[i:len(s)-1] + "ieth" |
|||
} else { |
|||
s = s[:i] + s[i:] + "th" |
|||
} |
|||
return s |
|||
} |
|||
// Below is a copy of https://rosettacode.org/wiki/Number_names#Go |
|||
var small = [...]string{"zero", "one", "two", "three", "four", "five", "six", |
|||
"seven", "eight", "nine", "ten", "eleven", "twelve", "thirteen", |
|||
"fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"} |
|||
var tens = [...]string{"", "", "twenty", "thirty", "forty", |
|||
"fifty", "sixty", "seventy", "eighty", "ninety"} |
|||
var illions = [...]string{"", " thousand", " million", " billion", |
|||
" trillion", " quadrillion", " quintillion"} |
|||
func say(n int64) string { |
|||
var t string |
|||
if n < 0 { |
|||
t = "negative " |
|||
// Note, for math.MinInt64 this leaves n negative. |
|||
n = -n |
|||
} |
|||
switch { |
|||
case n < 20: |
|||
t += small[n] |
|||
case n < 100: |
|||
t += tens[n/10] |
|||
s := n % 10 |
|||
if s > 0 { |
|||
t += "-" + small[s] |
|||
} |
|||
case n < 1000: |
|||
t += small[n/100] + " hundred" |
|||
s := n % 100 |
|||
if s > 0 { |
|||
t += " " + say(s) |
|||
} |
|||
default: |
|||
// work right-to-left |
|||
sx := "" |
|||
for i := 0; n > 0; i++ { |
|||
p := n % 1000 |
|||
n /= 1000 |
|||
if p > 0 { |
|||
ix := say(p) + illions[i] |
|||
if sx != "" { |
|||
ix += " " + sx |
|||
} |
|||
sx = ix |
|||
} |
|||
} |
|||
t += sx |
|||
} |
|||
return t |
|||
}</lang> |
|||
{{output}} |
|||
<pre>first |
|||
second |
|||
third |
|||
fourth |
|||
fifth |
|||
eleventh |
|||
sixty-fifth |
|||
one hundredth |
|||
one hundred first |
|||
two hundred seventy-second |
|||
twenty-three thousand four hundred fifty-sixth |
|||
eight quadrillion seven trillion six billion five million four thousand third</pre> |
|||
=={{header|Julia}}== |
=={{header|Julia}}== |
Revision as of 15:45, 14 June 2018
Ordinal numbers (as used in this Rosetta Code task), are numbers that describe the position of something in a list.
It is this context that ordinal numbers will be used, using an English-spelled name of an ordinal number.
The ordinal numbers are (at least, one form of them):
1st 2nd 3rd 4th 5th 6th 7th ··· 99th 100th ··· 1000000000th ··· etc
sometimes expressed as:
1st 2nd 3rd 4th 5th 6th 7th ··· 99th 100th ··· 1000000000th ···
For this task, the following (English-spelled form) will be used:
first second third fourth fifth sixth seventh ninety-nineth one hundredth one billionth
Furthermore, the American version of numbers will be used here (as opposed to the British).
2,000,000,000 is two billion, not two milliard.
- Task
Write a driver and a function (subroutine/routine ···) that returns the English-spelled ordinal version of a specified number (a positive integer).
Optionally, try to support as many forms of an integer that can be expressed: 123 00123.0 1.23e2 all are forms of the same integer.
Show all output here.
- Test cases
Use (at least) the test cases of:
1 2 3 4 5 11 65 100 101 272 23456 8007006005004003
- Related tasks
Go
As with the Kotlin solution, this uses the output of `say` from the Number_names task. <lang Go>import ( "fmt" "strings" )
func main() { for _, n := range []int64{ 1, 2, 3, 4, 5, 11, 65, 100, 101, 272, 23456, 8007006005004003, } { fmt.Println(sayOrdinal(n)) } }
var irregularOrdinals = map[string]string{ "one": "first", "two": "second", "three": "third", "five": "fifth", "eight": "eighth", "nine": "ninth", "twelve": "twelfth", }
func sayOrdinal(n int64) string { s := say(n) i := strings.LastIndexAny(s, " -") i++ // Now s[:i] is everything upto and including the space or hyphen // and s[i:] is the last word; we modify s[i:] as required. // Since LastIndex returns -1 if there was no space/hyphen, // `i` will be zero and this will still be fine. if x, ok := irregularOrdinals[s[i:]]; ok { s = s[:i] + x } else if s[len(s)-1] == 'y' { s = s[:i] + s[i:len(s)-1] + "ieth" } else { s = s[:i] + s[i:] + "th" } return s }
// Below is a copy of https://rosettacode.org/wiki/Number_names#Go
var small = [...]string{"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"} var tens = [...]string{"", "", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"} var illions = [...]string{"", " thousand", " million", " billion", " trillion", " quadrillion", " quintillion"}
func say(n int64) string { var t string if n < 0 { t = "negative " // Note, for math.MinInt64 this leaves n negative. n = -n } switch { case n < 20: t += small[n] case n < 100: t += tens[n/10] s := n % 10 if s > 0 { t += "-" + small[s] } case n < 1000: t += small[n/100] + " hundred" s := n % 100 if s > 0 { t += " " + say(s) } default: // work right-to-left sx := "" for i := 0; n > 0; i++ { p := n % 1000 n /= 1000 if p > 0 { ix := say(p) + illions[i] if sx != "" { ix += " " + sx } sx = ix } } t += sx } return t }</lang>
- Output:
first second third fourth fifth eleventh sixty-fifth one hundredth one hundred first two hundred seventy-second twenty-three thousand four hundred fifty-sixth eight quadrillion seven trillion six billion five million four thousand third
Julia
This makes use of code posted on this site by MichaeLeroy for a similar task at https://rosettacode.org/wiki/Number_names#Julia. The function num2text is used (but not included here) as posted from that location. <lang Julia> const irregular = Dict("one" => "first", "two" => "second", "three" => "third",
"five" => "fifth", "nine" => "ninth", "twelve" => "twelfth")
const suffix = "th" const ysuffix = "ieth"
function numtext2ordinal(s)
lastword = split(s)[end] redolast = split(lastword, "-")[end] if redolast != lastword lastsplit = "-" word = redolast else lastsplit = " " word = lastword end firstpart = reverse(split(reverse(s), lastsplit, limit=2)[end]) firstpart = (firstpart == word) ? "": firstpart * lastsplit if haskey(irregular, word) word = irregular[word] elseif word[end] == 'y' word = word[1:end-1] * ysuffix else word = word * suffix end firstpart * word
end
const testcases = [1 2 3 4 5 11 65 100 101 272 23456 8007006005004003] for n in testcases
println("$n => $(numtext2ordinal(num2text(n)))")
end </lang>
- Output:
1 => first 2 => second 3 => third 4 => fourth 5 => fifth 11 => eleventh 65 => sixty-fifth 100 => one hundredth 101 => one hundred and first 272 => two hundred and seventy-second 23456 => twenty-three thousand four hundred and fifty-sixth 8007006005004003 => eight quadrillion seven trillion six billion five million four thousand and third
Kotlin
This makes use of the code at https://rosettacode.org/wiki/Number_names#Kotlin, which I also wrote, and adjusts it to output the corresponding ordinal. Although, for good measure, the program can deal with negative integers, zero and UK-style numbers (the insertion of 'and' at strategic places, no 'milliards' I promise!) none of these are actually tested in line with the task's requirements. <lang scala>// version 1.1.4-3
typealias IAE = IllegalArgumentException
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 {
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 numToOrdinalText(n: Long, uk: Boolean = false): String {
if (n == 0L) return "zeroth" // or alternatively 'zeroeth' 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 ", " 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.toOrdinal()
}
fun numToOrdinalText(s: String, uk: Boolean = false): String {
val d = s.toDoubleOrNull() ?: throw IAE("String is not numeric") if (d !in Long.MIN_VALUE.toDouble() .. Long.MAX_VALUE.toDouble()) throw IAE("Double is outside the range of a Long Integer") val n = d.toLong() if (n.toDouble() != d) throw IAE("String does not represent a Long Integer") return numToOrdinalText(n, uk)
}
fun main(args: Array<String>) {
val la = longArrayOf(1, 2, 3, 4, 5, 11, 65, 100, 101, 272, 23456, 8007006005004003) println("Using US representation:") for (i in la) println("${"%16d".format(i)} = ${numToOrdinalText(i)}") val sa = arrayOf("123", "00123.0", "1.23e2") for (s in sa) println("${"%16s".format(s)} = ${numToOrdinalText(s)}")
}</lang>
- Output:
Using US representation: 1 = first 2 = second 3 = third 4 = fourth 5 = fifth 11 = eleventh 65 = sixty-fifth 100 = one hundredth 101 = one hundred first 272 = two hundred seventy-second 23456 = twenty-three thousand, four hundred fifty-sixth 8007006005004003 = eight quadrillion, seven trillion, six billion, five million, four thousand, third 123 = one hundred twenty-third 00123.0 = one hundred twenty-third 1.23e2 = one hundred twenty-third
Perl 6
This would be pretty simple to implement from scratch; it would be straightforward to do a minor modification of the Number names task code. Much simpler to just use the Lingua::EN::Numbers::Cardinal module from the Perl 6 ecosystem though. It easily handles ordinal numbers even though that is not its primary focus.
We need to be slightly careful of terminology. In Perl 6, 123, 00123.0, & 1.23e2 are not all integers. They are respectively an Int (integer), a Rat (rational number) and a Num (floating point number). For this task it doesn't much matter as the ordinal routine coerces its argument to an Int, but to Perl 6 they are different things. We can further abuse allomorphic types for some somewhat non-intuitive results as well.
It is not really clear what is meant by "Write a driver and a function...". Well, the function part is clear enough; driver not so much. Perhaps this will suffice.
<lang perl6>use Lingua::EN::Numbers::Cardinal;
printf( "\%16s : %s\n", $_, ordinal($_) ) for
- Required tests
|<1 2 3 4 5 11 65 100 101 272 23456 8007006005004003>,
- Optional tests
|<123 00123.0 1.23e2 123+0i 0b1111011 0o173 0x7B 861/7>;</lang>
- Output:
1 : first 2 : second 3 : third 4 : fourth 5 : fifth 11 : eleventh 65 : sixty-fifth 100 : one hundredth 101 : one hundred first 272 : two hundred seventy-second 23456 : twenty-three thousand, four hundred fifty-sixth 8007006005004003 : eight quadrillion, seven trillion, six billion, five million, four thousand third 123 : one hundred twenty-third 00123.0 : one hundred twenty-third 1.23e2 : one hundred twenty-third 123+0i : one hundred twenty-third 0b1111011 : one hundred twenty-third 0o173 : one hundred twenty-third 0x7B : one hundred twenty-third 861/7 : one hundred twenty-third
REXX
<lang REXX>/*REXX programs spells out ordinal numbers (in English, using the American system). */ numeric digits 3000 /*just in case the user uses gihugic #s*/ parse arg n /*obtain optional arguments from the CL*/
if n= | n="," then n= 1 2 3 4 5 11 65 100 101 272 23456 8007006005004003
pgmOpts= 'ordinal quiet' /*define options needed for $SPELL#.REX*/
do j=1 for words(n) /*process each of the specified numbers*/ x=word(n, j) /*obtain a number from the input list. */ os=$spell#(x pgmOpts) /*invoke REXX routine to spell ordinal#*/ say right(x, max(20, length(x) ) ) ' spelled ordinal number ───► ' os end /*j*/</lang>
- output when using the default inputs:
1 spelled ordinal number ───► first 2 spelled ordinal number ───► second 3 spelled ordinal number ───► third 4 spelled ordinal number ───► fourth 5 spelled ordinal number ───► fifth 11 spelled ordinal number ───► eleventh 65 spelled ordinal number ───► sixty-fifth 100 spelled ordinal number ───► one hundredth 101 spelled ordinal number ───► one hundred first 272 spelled ordinal number ───► two hundred seventy-second 23456 spelled ordinal number ───► twenty-three thousand four hundred fifty-sixth 8007006005004003 spelled ordinal number ───► eight quadrillion seven trillion six billion five million four thousand third
The $SPELL#.REX routine can be found here ───► $SPELL#.REX.
zkl
<lang zkl>fcn nth(n,th=True){
var [const] nmsth=T("","first","second","third","fourth","fifth","sixth","seventh","eighth","ninth"), nms1=T("","one","two","three","four","five","six","seven","eight","nine"), nms10=T("ten","eleven","twelve","thirteen","fourteen","fifteen","sixteen","seventeen","eighteen","nineteen"), nms10th=T("tenth","eleventh","twelfth","thirteenth","fourteenth","fifteenth","sixteenth","seventeenth","eighteenth","nineteenth"), nms20=T("twenty","thirty","forty","fifty","sixty","seventy","eighty","ninety"), nms1000=T("thousand","million","billion","trillion","quadrillion"); // 3,6,9,12,15
if (n<0) String("negative ",nth(-n,th)); else if(n<10) th and nmsth[n] or nms1[n]; else if(n<20) th and nms10th[n-10] or nms10[n-10]; else if(n<10) th and nmsth[n] or nms1[n]; else if(n<100){ m,txt := n%10,nms20[n/10-2]; if(m) String(txt,dash(n%10,"-",th)); else String(txt[0,-1],"ieth"); } else if(n<1000) String(nms1[n/100]," hundred",dash(n%100," ",th)); else{ n=n.toInt(); // yuck, only here to handle floats, 1.23-->"first" ds:=(n.numDigits()-1)/3*3; // 1e3->3, 1e4-->3, 1e5-->3, 1e6-->6, 1e7-->6 z:=(10).pow(ds); // 1234-->1000, 12345-->10000 thou:=ds/3 - 1; // 1000-->0, 10000-->0, 2,000,000-->1 nnn,ys := n/z, n%z; String((if(nnn<10) nms1[nnn] else nth(nnn,False)),
" ",nms1000[thou], if(ys==0) "th" else String(" ",nth(ys,th)));
}
} fcn dash(n,d,th){ if(n) String(d,nth(n,th)) else (th and "th" or "") }</lang> <lang zkl>testNs:=L(1,2,3,4,5,11,65,100,101,272,23456,8007006005004003,
123,00123.0,1.23e2,);
foreach n in (testNs){
if(n.isType(Float)) println("%16.2f --> %s".fmt(n,nth(n))); else println("%16d --> %s".fmt(n,nth(n)));
}</lang>
- Output:
1 --> first 2 --> second 3 --> third 4 --> fourth 5 --> fifth 11 --> eleventh 65 --> sixty-fifth 100 --> one hundredth 101 --> one hundred first 272 --> two hundred seventy-second 23456 --> twenty-three thousand four hundred fifty-sixth 8007006005004003 --> eight quadrillion seven trillion six billion five million four thousand third 123 --> one hundred twenty-third 123.00 --> one hundred twenty-third 123.00 --> one hundred twenty-third