数をフランス語の文字列表現に変換する (0-99)
(cinq の綴りが間違っていたので修正しました。2018.8.25)
数をフランス語で表したくなったので変換プログラムを書きます。言語は Scheme です。旧正書法に従った表記に変換されます。(正書法については次のサイトを参考にしました。 http://chiffre-en-lettre.fr/)
例えば 2018 は、deux mille dix-huit です(http://chiffre-en-lettre.fr/ecrire-nombre-2018-deux-mille-dix-huit リンク先では最初が大文字になっていますが、こっちでは小文字にします)。
0から16までの数、および60以下の10の倍数は単一の語で表されます。表引きできるように、これらの語の vector を定義しておきましょう。
;; 0から16までの数を表す語 (define fst17s #("zéro" "un" "deux" "trois" "quatre" "cinq" "six" "sept" "huit" "neuf" "dix" "onze" "douze" "treize" "quatorze" "quinze" "seize")) ;; 60以下の10の倍数を表す語 (define mul10s #("" "dix" "vingt" "trente" "quarante" "cinquante" "soixante"))
これらの語から、0から99までの数を表す言葉を構成することができます。
言葉で表したい数を n とします。
n が0から16までの数のときは、fst17s の n 番目の要素がそのまま n を表す言葉になります。
(define (convert<17 n) (vector-ref fst17s n))
n が17から69までの数のとき、n を表す言葉は、10の倍数のうち n を超えない範囲で最大である数と、n を10で割った余りとに対し、基本的にはそれぞれを表す語を "-" で繋いで構成されます。 ただし、余りが1のときは、"-" で繋ぐのではなく間に "et" を置きます。また、余りが0のときは「10の倍数のうち n を超えない範囲で最大である数」を表す語がそのまま n を表す言葉です。
(define convert<10 convert<17) (define (convert<70 n) (if (< n 17) (convert<17 n) (let ((qth (vector-ref mul10s (quotient n 10))) (r (modulo n 10))) (case r ((0) qth) ((1) (string-append qth " et un")) (else (string-append qth "-" (convert<10 r)))))))
*2
上の3つのコードを Schemoid(https://play.google.com/store/apps/details?id=magicgoose.schemoid)*3 に貼り付けて、いくつかの数でテストしてみると欲しい結果が得られました。
(convert<70 12) (convert<70 30) (convert<70 44) (convert<70 51)
↓
"douze" "trente" "quarante-quatre" "cinquante et un"
n が70から99までの数のとき、n を表す言葉は、20の倍数のうち n を超えない範囲で最大である数と、n を20で割った余りとに対し、基本的にはそれぞれを表す言葉*4を "-" で繋いで構成されます。
ただし、n が71のときだけは "-" で繋ぐのではなく間に "et" を置きます。
80以上を一緒にすると少しややこしくなるので先に80未満の変換プロシージャを定義してしまいましょう。
(define convert<20 convert<70) (define (convert<80 n) (if (< n 70) (convert<70 n) (string-append (vector-ref mul10s 6) (if (= n 71) " et " "-") (convert<20 (- n 60)))))
次は80以上です。
80は "quatre-vingts" です。
81から99までは、"quatre-vingt" *5と、n から80を引いて得られる数を表す言葉とを "-" で繋ぎます("et" は使いません)。
"quatre-vingt" は4を表す語である "quatre" と20を表す語である "vingt" を組み合わせた言葉であると考えることができます(4×20=80)。4と20を扱える変換プロシージャが既にあるので、下のコード内の %name-of-80 のように構成することもできます。同じ文字列をあちこちに書くと間違えそうなのでこういう変数を定義しました。*6
(define %name-of-80 (string-append (convert<10 4) "-" (convert<70 20))) (define (convert<100 n) (cond ((< n 80) (convert<80 n)) ((= n 80) (string-append %name-of-80 "s")) (else (string-append %name-of-80 "-" (convert<20 (- n 80))))))
ここまでのコードをまとめます。
(define fst17s #("zéro" "un" "deux" "trois" "quatre" "cinq" "six" "sept" "huit" "neuf" "dix" "onze" "douze" "treize" "quatorze" "quinze" "seize")) (define mul10s #("" "dix" "vingt" "trente" "quarante" "cinquante" "soixante")) (define (convert<17 n) (vector-ref fst17s n)) (define convert<10 convert<17) (define (convert<70 n) (if (< n 17) (convert<17 n) (let ((qth (vector-ref mul10s (quotient n 10))) (r (modulo n 10))) (case r ((0) qth) ((1) (string-append qth " et un")) (else (string-append qth "-" (convert<10 r))))))) (define convert<20 convert<70) (define (convert<80 n) (if (< n 70) (convert<70 n) (string-append (vector-ref mul10s 6) (if (= n 71) " et " "-") (convert<20 (- n 60))))) (define %name-of-80 (string-append (convert<10 4) "-" (convert<70 20))) (define (convert<100 n) (cond ((< n 80) (convert<80 n)) ((= n 80) (string-append %name-of-80 "s")) (else (string-append %name-of-80 "-" (convert<20 (- n 80))))))
これで100未満の数を変換できるようになったので全て変換して表示してみましょう。
(do ((i 0 (+ i 1))) ((>= i 100)) (display i) (display ":") (display (convert<100 i)) (newline))
上の2つのコードを Schemoid*7 に貼り付けると以下のように表示されます。
0:zéro 1:un 2:deux 3:trois 4:quatre 5:cinq 6:six 7:sept 8:huit 9:neuf 10:dix 11:onze 12:douze 13:treize 14:quatorze 15:quinze 16:seize 17:dix-sept 18:dix-huit 19:dix-neuf 20:vingt 21:vingt et un 22:vingt-deux 23:vingt-trois 24:vingt-quatre 25:vingt-cinq 26:vingt-six 27:vingt-sept 28:vingt-huit 29:vingt-neuf 30:trente 31:trente et un 32:trente-deux 33:trente-trois 34:trente-quatre 35:trente-cinq 36:trente-six 37:trente-sept 38:trente-huit 39:trente-neuf 40:quarante 41:quarante et un 42:quarante-deux 43:quarante-trois 44:quarante-quatre 45:quarante-cinq 46:quarante-six 47:quarante-sept 48:quarante-huit 49:quarante-neuf 50:cinquante 51:cinquante et un 52:cinquante-deux 53:cinquante-trois 54:cinquante-quatre 55:cinquante-cinq 56:cinquante-six 57:cinquante-sept 58:cinquante-huit 59:cinquante-neuf 60:soixante 61:soixante et un 62:soixante-deux 63:soixante-trois 64:soixante-quatre 65:soixante-cinq 66:soixante-six 67:soixante-sept 68:soixante-huit 69:soixante-neuf 70:soixante-dix 71:soixante et onze 72:soixante-douze 73:soixante-treize 74:soixante-quatorze 75:soixante-quinze 76:soixante-seize 77:soixante-dix-sept 78:soixante-dix-huit 79:soixante-dix-neuf 80:quatre-vingts 81:quatre-vingt-un 82:quatre-vingt-deux 83:quatre-vingt-trois 84:quatre-vingt-quatre 85:quatre-vingt-cinq 86:quatre-vingt-six 87:quatre-vingt-sept 88:quatre-vingt-huit 89:quatre-vingt-neuf 90:quatre-vingt-dix 91:quatre-vingt-onze 92:quatre-vingt-douze 93:quatre-vingt-treize 94:quatre-vingt-quatorze 95:quatre-vingt-quinze 96:quatre-vingt-seize 97:quatre-vingt-dix-sept 98:quatre-vingt-dix-huit 99:quatre-vingt-dix-neuf unspecified
*1:"dix" が被ってるけどまとめようとするとややこしくなるのでこのままにしておきます
*2:10未満の数を変換したい場合があるのに convert<10 がないのは変だと思うので定義しました。中身は convert<17 と完全に同じです。(一般の x に対して convert<x が x 以上の数を渡されたときにどう振る舞うかは決めていません)
*4:80は複合語で表されるので、「語」ではなく「言葉」と言っています。複合語を「語」と言ってもいいとは思いますが。
*5:後ろに数詞があるときは "s" がなく、数詞がないときは "s" がつきます。
*6:"quatre-vingts" は、"quatre" が、"vingts" にくっついてると考えるのが自然だとは思いますが、"quatre-vingt" という文字列があって "vingts" という文字列がないので、"quatre-vingt" に "s" をくっつけるほうが簡単です。
*7:Scheme Droid(https://play.google.com/store/apps/details?id=net.meltingwax.schemedroid)という別のアプリだと load が使えて便利ですが Scheme Droid は display で é が表示できません。