top recent

プログラミング:企画もの:お題3

関連ページ

プログラミング プログラミング:企画もの

目次

  1. お題3「漢数字」...
  2. 「漢数字」Scheme (Gauche) 版...
  3. 「漢数字」Haskell版(改訂)...
  4. tiny「漢数字」GikoForth版...
  5. 「漢数字」SqueakNihongo3 動けばいい版 改め それは桁が知っている…版 へ差し替え...
  6. 「漢数字」SqueakNihongo3 四則演算対応版...
  7. 「漢数字」SqueakNihongo3 再帰版...
  8. 「漢数字」SqueakNihongo3 壱行野郎版...

お題3「漢数字」

引数としてintegerをとり、漢数字の文字列を返す手続き
例:
1 => '壱'
13 => '拾参'
534 => '五百参拾四'
16,872 => '壱萬六千八百七拾弐'

[[id:1137]] 2002-09-05 16:19:43


「漢数字」Scheme (Gauche) 版


そういえばCommonLispには組込みで整数からその英語読みへの変換ルーチンがありますな。
このお題アルゴリズムはやっぱり再帰を使いたい。

- 修正:「恒河砂」以上は10^8進になるそうなので修正
- もひとつ修正:桁が大きく飛ぶ時の処理が甘かった。

(use text.tree)

(define (漢数字 整数)
(define 桁 '("" "" "弐" "参" "四" "伍" "六" "七" "八" "九"))
(define 小位列 '("千" "百" "拾" ""))
(define 大位列 '("" "万" "億" "兆" "京" "垓" "(禾予)" "穰" "溝" "澗" "正"
"載" "極"))
(define 特位列 '("恒河砂" "阿僧祇" "那由多" "不可思議" "無量大数"))
(define 壱恒河砂 (expt 10 52))
(define 限界 (expt 10 92))

(define (小再帰 数 単位 位列)
(cond ((zero? 数) '())
((= 数 1) '("壱"))
((>= 数 単位)
`(,(list-ref 桁 (quotient 数 単位)) ,(car 位列)
,@(小再帰 (modulo 数 単位) (/ 単位 10) (cdr 位列))))
(else (小再帰 数 (/ 単位 10) (cdr 位列)))))

(define (大再帰 数 位列)
`(,@(if (>= 数 10000)
(大再帰 (quotient 数 10000) (cdr 位列))
'())
,@(if (zero? (modulo 数 10000))
'()
`(,(小再帰 (modulo 数 10000) 1000 小位列) ,(car 位列)))))

(define (特大再帰 数 位列)
`(,@(if (>= 数 100000000)
(特大再帰 (quotient 数 100000000) (cdr 位列))
'())
,@(if (zero? (modulo 数 100000000))
'()
`(,(大再帰 (modulo 数 100000000) 大位列) ,(car 位列)))))

(tree->string
(cond ((>= 整数 限界) "無限")
((>= 整数 壱恒河砂)
(list (特大再帰 (quotient 整数 壱恒河砂) 特位列)
(大再帰 (modulo 整数 壱恒河砂) 大位列)))
(else (大再帰 整数 大位列))))
)

実行例
gosh> (漢数字 8475293847503984572098457023847502398457230845743503495820349850238590234952938457923754)
"八千四百七拾伍無量大数弐千九百参拾八万四千七百伍拾不可思議参千九百八拾四万伍千七百弐拾那由多九千八百四拾伍万七千弐拾参阿僧祇八千四百七拾伍万弐百参拾九恒河砂八千四百伍拾七極弐千参百八載四千伍百七拾四正参千伍百参澗四千九百伍拾八溝弐千参拾四穰九千八百伍拾(禾予)弐千参百八拾伍垓九千弐拾参京四千九百伍拾弐兆九千参百八拾四億伍千七百九拾弐万参千七百伍拾四"
gosh> (漢数字 1000000001)
"拾億壱"
gosh> (漢数字 100000000100000000001)
"壱垓千億壱"

[[id:1138]] 2002-09-05 18:06:59


「漢数字」Haskell版(改訂)


恒河沙以上に対応していない^^;<--- 対応しました。--Haskeller

#!/usr/bin/env runhugs
\begin{code}
module Main where

import System
import List

main = do { a:_ <- getArgs
; putStrLn $ toKanSuuji a
}

toKan,toKan' :: String -> String
toKan rstr
= concat
$ reverse
$ zipWith mkname base10000
$ map (concat . reverse . conv4)
$ groupOf 4
$ rstr

toKan' rstr
= concat
$ reverse
$ zipWith mkname baseBig
$ map toKan
$ groupOf 8
$ rstr

toKanSuuji str
= case splitAt 52 $ reverse str of
(s,"") -> toKan $ padding 4 s
(s,b) -> if length b > 40
then "こんな大きな数は日本語では名前はありません!"
else (toKan' $ padding 8 b) ++ toKan s
where
padding n s = s ++ replicate ((n - (length s `mod` n)) `mod` n) '0'

groupOf _ [] = []
groupOf n xs = take n xs : groupOf n (drop n xs)

base1 = ["","壱","弐","参","四","五","六","七","八","九"]
base10 = ["","拾","百","阡"]
base10000 = ["","萬","億","兆","京","垓","禾予","穣","溝","澗","正","載","極"]
baseBig = ["恒河沙","阿僧祇","那由他","不可思議","無量大数"]

conv4 rstr = zipWith mkname' base10
$ map ((base1 !!) . read . (flip (:) [])) rstr

mkname "" "" = ""
mkname "" d = d
mkname p "" = ""
mkname p d = d++p

mkname' "" "" = ""
mkname' "" d = d
mkname' p "" = ""
mkname' p "壱" = p
mkname' p d = d++p
\end{code}

[[id:1140]] 2002-09-05 23:56:14


tiny「漢数字」GikoForth版


|[ "" " " "弐" "参" "四" "伍" "六" "七" "八" "九" ]| value 桁
|[ "千" "百" "拾" "" ]| value 位
|[ "極" "載" "正" "澗" "溝" "穰" "禾予" "垓" "京" "兆" "億" "萬" "" ]| value 大位

: n>v { n k -- v }
n :[ k /mod ?dup IF <recurse ELSE |[ ]|| THEN :: ::add ]: :. ;

: n>k { n | v -- v }
n 10 /mod 10 n>v :[ 桁 ::at ]: ::map! -> v
dup 1 = IF drop "壱" ELSE 桁 ::at THEN v :: ::add ;

: keta { su kn | sz -- s }
su ::size -> sz
sz 0= IF "" ELSE sz 1 = IF kn ELSE su kn ::+ THEN THEN ;

: (n>ks) ( n -- s )
n>k dup ::size 4 swap - 位 ::drop :[ keta ]: ::zipWith :[ ::+ ]: ::foldl1 ;

: n>ks { n | tmp -- s }
n 10000 n>v :[ (n>ks) ]: ::map! -> tmp
tmp 大位 ::size tmp ::size - 大位 ::drop :[ keta ]: ::zipWith :[ ::+ ]: ::foldl1 ;

$ 7fffffff n>ks ::print
弐拾壱億四千七百四拾八萬参千六百四拾七 ok

↑4byteの限界‥‥‥(´・ω・`)ショボーン。。。
- "垓" "京"が入れ代わってますた。(意味ないけど‥‥鬱)

[[id:1142]] 2002-09-06 00:15:33


「漢数字」SqueakNihongo3 動けばいい版 改め それは桁が知っている…版 へ差し替え

日本語版 Squeak (3.2 ベース) → http://squeak.hoops.ne.jp/
日本語版 Squeak の初心者向け機能制限を解除する手順 → http://sumim.no-ip.com:8080/morphiclesson/22
数の単位の文献 → http://village.infoweb.ne.jp/~fxba0016/misc/suumei/suumei.html

このソースの実行の仕方
-「'From Squeak ...」 から「... asString "'壱不可思議壱'"! !」までをブラウザでコピー
- Squeakで新しい Workspace を開いてペースト
- ALT + a ですべてを選択
- 右クリックメニュー(ボタンの色忘れた(^^;)から一番下の "more..." (黄ボタンです。赤ボタン、黄ボタン、青ボタン( Smalltalk-80 GUI デザイン) (UI)--sumim)
- 更に出てくるメニューから "file it in (G)"
- システムブラウザに "Goodies-漢数字" ができている!
フォロー、痛み入ります。ペーストしたときに文字化けしたりする場合があるので、チェンジセットファイルを作りました。これをダウンロードして次の手順に従っても同じことができます。
http://sumim.no-ip.com:8000/swiki/uploads/wiki/456/KansuujiArray.cs
- SqueakNihong3.image と同じディレクトリに KansuujiArray.cs を置く
- 上の機能限定解除を行なう
- 右の Tools フラップから File List をドラッグ&ドロップ
- 右上のペインで KansuujiArray.cs を選択
- 右上ボタン列から File-in to New をクリック
----
'From Squeak 3.2 of 11 July 2002 [latest update: #4917] on 7 September 2002 at 11:38:03 pm'!
Object subclass: #'漢数字桁'
instanceVariableNames: '数字 配列 桁 '
classVariableNames: ''
poolDictionaries: ''
category: 'Goodies-漢数字'!
Array variableSubclass: #'漢数字配列'
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Goodies-漢数字'!
漢数字配列 class
instanceVariableNames: ''!

!Object methodsFor: 'converting' stamp: 'sumim 9/7/2002 23:03'!
as漢数字配列
^ self printString asInteger asString as漢数字配列! !


!漢数字桁 methodsFor: 'accessing' stamp: 'sumim 9/7/2002 21:21'!

^ 桁! !

!漢数字桁 methodsFor: 'accessing' stamp: 'sumim 9/7/2002 20:03'!
桁: 数値
桁 _ 数値! !

!漢数字桁 methodsFor: 'accessing' stamp: 'sumim 9/7/2002 21:21'!
数字
^ 数字! !

!漢数字桁 methodsFor: 'accessing' stamp: 'sumim 9/7/2002 20:03'!
数字: 文字
数字 _ 文字! !

!漢数字桁 methodsFor: 'accessing' stamp: 'sumim 9/7/2002 21:21'!
配列
^ 配列! !

!漢数字桁 methodsFor: 'accessing' stamp: 'sumim 9/7/2002 20:01'!
配列: ある配列
配列 _ ある配列! !

!漢数字桁 methodsFor: 'printing' stamp: 'sumim 9/7/2002 23:31'!
asString
| 文字列 十進単位 万進単位 |
文字列 _ (#(零 壱 弐 参 四 五 六 七 八 九) at: 数字 asString asInteger + 1) asString.
十進単位 _ (#(#'' 拾 百 千) at: 桁 - 1 \\ 4 + 1) asString.
万進単位 _ (#(#'' 萬 億 兆 京 垓 禾予 穣 溝 澗 正 戴 極 萬
恒河砂 萬 阿僧祇 萬 那由他 萬 不可思議 萬
無量 萬 大数 萬) at: 桁 // 4 + 1 ifAbsent: [#〓]) asString.
(数字 == $0 and: [配列 size > 1]) ifTrue: [文字列 _ ''. 十進単位 _ ''].
(数字 == $1 and: [桁 \\ 4 ~~ 1]) ifTrue: [文字列 _ ''].
(桁 \\ 8 == 1 and: [桁 // 4 > 11])
ifTrue: [(self 単位無用: 8) ifTrue: [万進単位 _ '']]
ifFalse: [(桁 \\ 4 ~~ 1 or: [self 単位無用: 4 ]) ifTrue: [万進単位 _ '']].
^ 文字列, 十進単位, 万進単位! !

!漢数字桁 methodsFor: 'printing' stamp: 'sumim 9/7/2002 20:55'!
printOn: aStream
aStream nextPut: $'.
aStream nextPutAll: self asString.
aStream nextPut: $'! !

!漢数字桁 methodsFor: 'printing' stamp: 'sumim 9/7/2002 23:30'!
単位無用: 4か8
^ ((桁 to: (桁 + 4か8 - 1 min: 配列 size))
select: [ :idx | (配列 at: 配列 size - idx + 1) 数字 ~~ $0 ]) size = 0! !


!SequenceableCollection methodsFor: 'converting' stamp: 'sumim 9/7/2002 23:08'!
as漢数字配列
"Answer an 漢数字配列 whose elements are the elements of the receiver."

^ 漢数字配列 withAll: self asInteger asString! !


!漢数字配列 methodsFor: 'private' stamp: 'sumim 9/7/2002 20:06'!
replaceFrom: start to: stop with: replacement startingAt: repStart
| index repOff |
repOff _ repStart - start.
index _ start - 1.
[(index _ index + 1) <= stop]
whileTrue: [self at: index put:
(漢数字桁 new
配列: self;
数字: (replacement at: repOff + index);
桁: self size - index + 1)]! !

!漢数字配列 methodsFor: 'converting' stamp: 'sumim 9/7/2002 21:56'!
asString
| stream |
stream _ WriteStream on: (String new: 200).
self do: [:each | stream nextPutAll: each asString ].
^ stream contents! !


!漢数字配列 class methodsFor: 'examples' stamp: 'sumim 9/7/2002 23:33'!

^ 100000000000000000000000000000000000000000000000000000000000000000000000000000001 as漢数字配列 asString "'壱不可思議壱'"! !
----
1234567890 as漢数字配列 asString "'拾弐億参千四百五拾六萬七千八百九拾'"

漢数字配列 の要素、漢数字桁 は自分の桁位置や周りの様子からどう表示されるべきか自分で判断します。--sumim
- オブジェクト指向って感じ。かっちょいいですね! --SHIMADA
- 環境を巻き込んで、「こんなのどうかな…」という実験が気軽にできるのは閉じた世界ならではですね。--sumim
- なるほど… なんとなく理解した所によれば、自分自身(及びその部分)へ再帰的にメッセージを送ってゆくって感じですか。Scheme版と比べると、クロージャとオブジェクトの二つの立場を象徴しているような。--Schemer
-- 「> 象徴している」。御意。オブジェクトは機能を持ったデータ間通信による連鎖的広がりを形成し、クーロジャはデータをもった機能の帰納的広がりを形成する…というので合っていますでしょうか? 一方Lispの接着力は… (プログラミング言語:糊)とかプログラムはRunするものなのか? (プログラミング)とかと関係がありそうな、なさそうな…。--sumim
--- 接着とかいうと、アラン・ケイの“ma”のを思い出します。--sumim
- このプログラムは Smalltalk-80 での画面表示の“作法”を知らないと読み解くには難しいかも知れません(冒頭にチェンジセットとあるように、ある意味、差分に過ぎないので…)。具体的には、
-- オブジェクトは皆、そのスーパークラスの Object からの継承で printOn: というメッセージを受けたとき自分が何者かを文字列で表わす仕組みを持っています。デフォでは単に「a/an オブジェクト名」ですが、Array や String などはこのメソッドをオーバーライドして自分自身やその要素の列を返しています。漢数字桁も同様にこれをオーバーライドして自分にふさわしい文字列を吐きだします。
参考)Object の #printOn: の定義
printOn: aStream
| title |
title _ self class name.
aStream
nextPutAll: (title first isVowel ifTrue: ['an '] ifFalse: ['a ']);
nextPutAll: title
-- 何がふさわしいかは、自分のホルダである漢数字配列内での位置や自分が含まれる“万単位ブロック”に属する別の漢数字桁の様子を調べて判断します。自分の中に自分の位置やホルダ参照のためのインスタンス変数を持っているのは私的には美しくなくて、Smalltalk-80 がそのオブジェクト(インスタンス)にあまり機能を与えていない制限をもろにかぶったかたちです。(もっとも、そんなことまで知っている“全知”のインスタンスなんぞとのお付き合いは、遠慮ねがいたいものです(笑))
-- そんなわけで printOn: が繰り返し出てきますが、再帰はしていません。システムが漢数字配列に printOn: を送って、漢数字配列が自分を表示するために、その要素である漢数字桁に printOn: を送ってその結果を得る、その時に桁は自らにふさわしい文字列を返す…といった流れなのでどちらかというとイベント駆動型の単調(^_^;)なカスケードです。
-- このプログラムの妙というか楽しむのには、やはり、インスペクタで漢数字配列を覗いて(1234 as漢数字配列 inspect)、中身をいじっていただくのが一番だと思います。別途漢数字桁のインスペクタを開いておいて(左ペインのダブルクリック)、その「数字」を右ペインで別の数字に変えたとき、漢数字配列のインスペクタの self の表示がどうなるのか…などといったあたりでしょうか。--sumim
http://sumim.no-ip.com:8000/swiki/uploads/wiki/456/kansuujiarray1.gif
http://sumim.no-ip.com:8000/swiki/uploads/wiki/456/kansuujiarray2.1.gif

-- 解説ありがとうございます。そうか、漢数字配列→漢数字桁の2階層しかないんですね。各漢数字桁は自分の桁を知っている、という理解でよろしいんでしょうか。--Schemer
-- そうです。漢数字配列を作るときに、その要素各々の漢数字桁内に用意した「桁」というインスタンス変数にその情報が入っていていつでも参照できます。設計段階(ってほどでもないですが…)のイメージでは、漢数字桁は自分の位置も漢数字配列に問い合わせることで分かる…ようにしたかったのですが、自分を含む漢数字列もその中に含まれる自分の位置も一意に決めることができなかったのでこういう仕様にしました。なので、インスペクタで玩べるのは実質「数字」だけ(それも $0 〜 $9 までの Character )で、「桁」や「配列」に手を加えてしまうとおかしなことになります。ここがこの実装の“弱っちい”ところです。--sumim

[[id:1143]] 2002-09-11 08:04:17


「漢数字」SqueakNihongo3 四則演算対応版

意味があるかどうかは別にして…(ぉ。--sumim

100 as漢数字 "--> ('百' as漢数字)"
'一京壱百参' as漢数字 asInteger "--> 10000000000000103"
'一萬無量参拾四' as漢数字 + '三十萬四千七百参拾弐' as漢数字 "--> ('壱萬無量参拾萬四千七百六拾六' as漢数字)"
'四拾七万三千四大数弐拾四極三十二万四千三百弐拾壱' as漢数字 / 3 "--> ('拾五萬七千六百六拾八大数八極拾萬八千百七' as漢数字)"
1234 / '六百壱拾七' as漢数字 "--> 2"

http://sumim.no-ip.com:8000/swiki/uploads/wiki/456/KansuujiKeisan.cs.gz

[[id:1169]] 2002-09-12 00:52:02


「漢数字」SqueakNihongo3 再帰版

Integer のインスタンスメソッドとして定義します。
バグがあったのと、メソッド名の座りが悪かったので変えたのと、位取りを固定したので差し替えました。--sumim
----
as漢数字文字列

self < 0 ifTrue: [^ '負の', self abs as漢数字文字列 ].

self < 10 ifTrue: [
^ (#(零 壱 弐 参 四 五 六 七 八 九) at: self abs + 1) asString].

self < 10000 ifTrue: [
^ (self asString asArray collectWithIndex: [ :itm :idx |
(itm asString asInteger as漢数字文字列 = '零'
ifTrue: [''] ifFalse: [
((idx < self asString size
and: [itm asString asInteger as漢数字文字列 = '壱'])
ifTrue: [''] ifFalse: [itm asString asInteger as漢数字文字列]),
(#(#'' 拾 百 千) at: (self asString size - idx + 1)) asString])])
asStringWithCr copyReplaceAll: String cr with: ''].

self < (10 raisedTo: 4 * 12) ifTrue: [
^ (((self asString size to: 1 by: -4) collect: [ :idx |
self asString copyFrom: (idx - 3 max: 1) to: idx ])
collectWithIndex: [ :itm :idx |
(itm asInteger > 0 ifTrue: [itm asInteger as漢数字文字列] ifFalse: ['']),
((#(#'' 萬 億 兆 京 垓 禾予 穣 溝 澗 正 戴))
at: idx) asString])
reverse asStringWithCr copyReplaceAll: String cr with: ''].

self < (10 raisedTo: 4 * 26) ifTrue: [
^ ((((self asString size to: 1 by: -8) collect: [ :idx |
self asString copyFrom: (idx - 7 max: 1) to: idx ]) collectWithIndex: [ :itm :idx |
idx > 6 ifFalse: [''] ifTrue: [
(itm asInteger > 0 ifTrue: [itm asInteger as漢数字文字列] ifFalse: ['']),
((#(極 恒河砂 阿僧祇 那由他 不可思議 無量 大数) at: idx - 6) asString)]])
reverse asStringWithCr copyReplaceAll: String cr with: ''),
((self \\ 10e47) as漢数字文字列)].

self error: ['変換できません']

"12345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678 as漢数字文字列"

[[id:1146]] 2002-09-06 14:23:16


「漢数字」SqueakNihongo3 壱行野郎版

万万進法、無量大数諸説にはコメントアウトで対応しました(ぉ--sumim
----
(((((100000000000000000000000000000000000000000000000000000000000000000000001
asString asArray collectWithIndex: [ :itm :idx |
(#(#'' 壱 弐 参 四 五 六 七 八 九) at: itm asString asInteger + 1) asString])
reverse collectWithIndex: [ :itm :idx |
((itm = '壱' and: [idx \\ 4 ~~ 1]) ifTrue: [''] ifFalse: [itm]),
(itm = '' ifFalse: [(#(#'' 拾 百 千) at: idx - 1 \\ 4 + 1) asString] ifTrue: ['']),
(idx \\ 4 == 1 ifTrue: [
'*', ((#(#'' 万 億 兆 京 垓 杼 "...(禾予)の代替"
穣 溝 澗 正 戴 極 万+ 恒河砂 万+ 阿僧祇 "...(禾氏)の代替"
万+ 那由他 万+ 不可思議 万+ 無量 万+ 大数 万+)
" 無量と大数を区別しないときは、'無量 万+ 大数' 部分を '無量大数' に "
" copyWithout: #万+ " "...万万進法でないときコメントアウトを復活"
) at: idx // 4 + 1) asString, ' '] ifFalse: [''])])
reverse asStringWithCr copyWithout: Character cr)
copyReplaceAll: '+ *' with: '')
substrings select: [ :itm | itm first ~= $* ] thenCollect: [ :itm | (itm copyWithout: $*) copyWithout: $+])
asStringWithCr copyWithout: Character cr

[[id:1147]] 2002-09-06 17:54:46


top recent

HashedWiki version 3 beta
SHIMADA Keiki