challenge 整数の漢数字表記

キーボードから正の整数を入力すると、それを漢数字で表示するプログラムを作ってください☆ 例えば「1732050807568877」なら「千七百三十二兆 五百八億 七百五十六万 八千八百七十七」といった感じです☆ 「一七三二兆 〇五〇八億 〇七五六万 八八七七」ではダメですよ^^;

このお題は匿名での投稿です。 与えられる整数の範囲は一京未満(10000000000000000未満)としたいと思います。 ご投稿ありがとうございます。

2年前のLL Day&Nightの「キミならどう書く」で、 これ専用のCPANモジュールが作られていたような記憶があるので 勝手にPerlからの挑戦状とみなしておきます(笑)

Posted feedbacks - Nested

Flatten Hidden
初の1番乗り?
きれいなコードではないですが...

> perl suuzi.pl 1732050807568877
千七百三十二兆五百八億七百五十六万八千八百七十七

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#!/usr/bin/perl

my $num = $ARGV[0];

my @name0 = ('', qw(一 二 三 四 五 六 七 八 九));
my @name1 = ('', qw(十 百 千));
my @name2 = ('', qw(万 億 兆));

my $i = 0;
my $j = 0;
my $res = '';
while($num =~ s/(\d)$//) {
  my $d = $1;
  if($d > 1) {
    $res = $name0[$d] . $name1[$i] . $res;
  }
  elsif($d == 1 and $i != 0) {
    $res = $name1[$i] . $res;
  }
  $i++;
  if($i % 4 == 0) {
    $j++;
    $i = 0;
    $res = $name2[$j] . $res if($num ne '');
  }
}

print $res, "\n";
バグがありました(汗
1の位の1の処理条件が抜けていました.

シャワー浴びていたときにふと気づきましたが,手遅れ.

というわけで一番乗りは無かったことにしていただけないでしょうか...(恥
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#!/usr/bin/perl

my $num = $ARGV[0];

my @name0 = ('', qw(一 二 三 四 五 六 七 八 九));
my @name1 = ('', qw(十 百 千));
my @name2 = ('', qw(万 億 兆));

my $i = 0;
my $j = 0;
my $res = '';
while($num =~ s/(\d)$//) {
  my $d = $1;
  if($d > 1) {
    $res = $name0[$d] . $name1[$i] . $res;
  }
  elsif($d == 1 and $i != 0) {
    $res = $name1[$i] . $res;
  }
  elsif($d == 1 and $i == 0) {
    $res = $name0[$d] . $res;
  }
  $i++;
  if($i % 4 == 0) {
    $j++;
    $i = 0;
    $res = $name2[$j] . $res if($num ne '');
  }
}

print $res, "\n";
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
def num2K(num):
  if num == 0: return u"零"
  s = []
  
  for i,v in enumerate(map(int, reversed(str(num)))):
    if i in num2K.table:
      s.append(num2K.table2[v]+num2K.table[i]+(i > 3 and u" " or u""))
    elif v > 1:
      s.append(num2K.table2[int(v)]+num2K.table[i%4])
    elif v != 0:
      s.append(num2K.table[i%4])
  return "".join(reversed(s))
num2K.table = {0:u"", 1:u"十",2:u"百",3:u"千",4:u"万",8:u"億",12:u"兆",16:u"京"}
num2K.table2 = {0:u"",1:u"一",2:u"二",3:u"三",4:u"四", 5:u"五", 6:u"六", 7:u"七", 8:u"八", 9:u"九"}



print num2K(input(u">>"))
>>10001
一万 千百十一

と表示されます。
うわーすみません。これで大丈夫かな? あと、1000と100は千、百、でいいんですよね、これ。でも一千はありなのかな。とりあえず、10000からは一万とかにしました。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def num2K(num):
  if num == 0: return u"零"
  s = []
  
  for i,v in enumerate(map(int, reversed(str(num)))):
    if v == 0: 
      continue
    if i in num2K.table:
      if v > 1 or i > 3:
        s.append(num2K.table2[v]+num2K.table[i]+(i > 3 and u" " or u""))
      else:
        s.append(num2K.table[i] or num2K.table2[v]+(i > 3 and u" " or u""))
    elif v > 1:
      s.append(num2K.table2[int(v)]+num2K.table[i%4])
    elif v != 0:
      s.append(num2K.table[i%4])
  return "".join(reversed(s))
num2K.table = {0:u"", 1:u"十",2:u"百",3:u"千",4:u"万",8:u"億",12:u"兆",16:u"京"}
num2K.table2 = {0:u"",1:u"一",2:u"二",3:u"三",4:u"四", 5:u"五", 6:u"六", 7:u"七", 8:u"八", 9:u"九"}



print num2K(input(u">>"))
さらに突き詰めてみました。たぶんいけているハズ。 この方法だと、どれだけ単位が増えても単純にテーブルに追加するだけでいけます。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
def num2kanji(num):
  if num == 0: return u"零"
  num_str = str(num)[::-1]
  s = []
  for n,v in enumerate(map(int, num_str)):
    i = n if n in num2kanji.table else n%4
    sep = i>3 and u" "or""
    if v == 0 and i > 3 and num_str[n:n+4]!=u"0000":
      s.append(num2kanji.table[i]+sep)
    elif v > 1 or (i > 3 and v == 1):
      s.append(num2kanji.table2[v]+num2kanji.table[i]+sep)
    elif v > 0:
      s.append(num2kanji.table[i] or num2kanji.table2[v]+sep)
  return "".join(reversed(s)).rstrip()

num2kanji.table = {0:u"", 1:u"十",2:u"百",3:u"千",4:u"万",8:u"億",12:u"兆",16:u"京", 20:u"垓",24:u"予", 28:u"穣", 32:u"溝", 36:u"潤", 40:u"正", 44:u"載", 48:u"極", 52:u"恒河沙", 56:u"阿僧祇", 60:u"那由多", 64:u"不可思議", 68:u"無量大数"}
num2kanji.table2 = {0:u"",1:u"一",2:u"二",3:u"三",4:u"四", 5:u"五", 6:u"六", 7:u"七", 8:u"八", 9:u"九"}

print num2kanji(input(u">>"))
「恒河沙」以降が10^8ステップになる流儀があるので、もうちょっと面倒では?
ありゃそうなんですか・・・日本の単位も奥深いですねえ。
日本語コケるのでエスケープしてます。こういうときのためにソースコードのASCII以外をエスケープするスクリプトを作ってあるのですが、役に立ちました。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
object num2K {
  val table = Map(0->"", 1->"\u5341",2->"\u767e",3->"\u5343",4->"\u4e07",8->"\u5104",12->"\u5146",16->"\u4eac")
  val table2 = Map(0->"",1->"\u4e00",2->"\u4e8c",3->"\u4e09",4->"\u56db", 5->"\u4e94", 6->"\u516d", 7->"\u4e03", 8->"\u516b", 9->"\u4e5d")
  def apply(num:long):String = {
    if(num == 0) return "\u96f6"
    val lst = num.toString.reverse.map(x=>Integer.parseInt(x+""))
    (0 to lst.length-1).foldLeft(List("")){(r, i) => 
      if(table.contains(i)) {
        table2(lst(i))+table(i)+(if(i>3){" "}else{""})::r
      }else if(lst(i) > 1){
        table2(lst(i))+table(i%4)::r
      }else if(lst(i) != 0){
        table(i%4)::r
      }else{
        r
      }
    }.mkString("")
  }
}

print(">>")
println(num2K(readLong))
ミスってたので修正。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
object num2K {
  val table = Map(0->"", 1->"\u5341",2->"\u767e",3->"\u5343",4->"\u4e07",8->"\u5104",12->"\u5146",16->"\u4eac")
  val table2 = Map(0->"",1->"\u4e00",2->"\u4e8c",3->"\u4e09",4->"\u56db", 5->"\u4e94", 6->"\u516d", 7->"\u4e03", 8->"\u516b", 9->"\u4e5d")
  def apply(num:long):String = {
    if(num == 0) return "\u96f6"
    val numStr = num.toString.reverse.mkString("")
    val lst = numStr.map(x=>Integer.parseInt(x+""))

    (0 to lst.length-1).foldLeft(List("")){(r, n) => 
      val i = (if(table.contains(n)){n}else{n%4})
      val sep = (if(i>3){" "}else{""})
      val v = lst(n)
      if(v==0 && i>3 && !numStr.substring(n).startsWith("0000")) 
        table(i)::sep::r
      else if(v > 1 || (i > 3 && v == 1))
        table2(v)+table(i)::sep::r
      else if(v > 0)
        (if(table(i) == ""){table2(v)}else{table(i)})::sep::r
      else
        r
    }.mkString("").trim
  }
}

while(true) {
  print(">>")
  println(num2K(readLong))
}
途中からわけがわからなくなりましたが、多分できました。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class Test {
	public static void main(String[] args) {
		System.out.println("args[0] = " + args[0]);
		String res = num2K(args[0]);
		System.out.println(res);
	}
	static String[] unitStr = new String[] { "", "万", "億", "兆", /*"京", "垓", 
	"穰", "溝", "澗", "正", "載", "極",
	"恒河沙", "阿僧祇", "那由他", "不可思議", "無量大数",*/ }; 
	static String[] unitStr2 = new String[] { "千", "百", "十", };
	static String numStr = "零一ニ三四五六七八九";
	public static String num2K(long num) {
		String s = Long.toString(num);
		return num2K(s);
	}
	public static String num2K(String s) {
		if (s.length() == 1) {
			return Character.toString(numStr.charAt(s.charAt(0) - '0'));
		}
		StringBuffer sb = new StringBuffer();
		boolean flag = false;
		for (int i = 0; i < s.length(); i++) {
			int mod = 3 - (s.length() - i - 1) % 4;
			int div = (s.length() - i - 1) / 4;
			if (div >= unitStr.length) {
				return null;
			}
			int num = s.charAt(i) - '0';
			flag |= num != 0;
			
			if (num == 0 && (mod != 3 || div != 0) && (!flag || mod != 3)) {
				continue;
			}
			if (num == 0 || num == 1 && mod != 3 && !(mod == 0 && div != 0)) {
			} else {
				sb.append(numStr.charAt(num));
			}
			if (mod == 3) {
				sb.append(unitStr[div]);
				flag = false;
			} else {
				sb.append(unitStr2[mod]);
			}
		}
		return sb.toString();
	}
}
多分できました。終了するときは、CTRL+Zを押してリターンキーを押します。(Windows以外ではもしかしたら違うキーかも)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# coding: shift_jis

class SolveError(RuntimeError):
    pass

def split(n, m):
    while n:
        yield n % m
        n //= m

def solve(s):
    try:
        n = int(s)
    except ValueError:
        raise SolveError("not an integer")
    if not(0 < n < 10000000000000000):
        raise SolveError("not in range")

    large = [u"", u"万", u"億", u"兆"]
    small = [u"", u"十", u"百", u"千"]
    digit = [u"", u"一", u"二", u"三", u"四", u"五", u"六", u"七", u"八", u"九"]
    def convert():
        for i_large, n_large in enumerate(split(n, 10000)):
            if n_large:
                if i_large:
                    yield u" "
                yield large[i_large]
                for i_small, n_small in enumerate(split(n_large, 10)):
                    if n_small:
                        yield small[i_small]
                        if not(n_small == 1 and i_small):
                            yield digit[n_small]
    return "".join(reversed(list(convert())))

def main():
    while 1:
        try:
            s = raw_input("> ")
        except EOFError:
            break
        try:
            print solve(s)
        except SolveError, e:
            print "error:", e

if __name__ == '__main__':
    main()

	
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
function num2K(num){
  table = {0=>"", 1=>"十",2=>"百",3=>"千",4=>"万",8=>"億",12=>"兆",16=>"京"}
  table2 = ["","一","二","三","四","五","六","七","八","九"]
  if (num == 0) return "零"
  i = 0
  s = {}
  for (v : project(reverse(string(num)), {a->a-'0'})){
    if (table.containsKey(i)){
      s.add(table2[v] + table[i] + (i > 3 ? " " : "" ))
    } else if (v > 1){
      s.add(table2[v] + table[i%4])
    } else if (v != 0){
      s.add(table[i%4])
    }
    i++
  }
  join("", reverse(s))
}

print(">>"); flush(); println(num2K(readLine(System.in))) 
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
(use text.tree)
(use util.list)

(define (positive->kansuji n)
  (define (keta-split n keta)
    (receive (q r) (quotient&remainder n keta)
             (if (< q keta)
                 (if (zero? q)
                     (list r)
                     (list r q))
                 (cons r (keta-split q keta)))))
  (define (add-tanni lst tanni)
    (fold
     (lambda (x t l)
       (cons (list (cond ((and (equal? 1 x) (not (equal? t ""))) "")
                         ((integer? x)
                          (list-ref '("" "一" "二" "三" "四" "五" "六" "七" "八" "九") x))
                         (else x))
                   (if (and (equal? 0 x) (not (equal? t "")))
                       ""
                       t))
             l))
     '() lst tanni))
  (tree->string
   (intersperse " " (add-tanni (map (lambda (n)
                                      (add-tanni (keta-split n 10)
                                                 '("" "十" "百" "千")))
                                    (keta-split n 10000))
                               '("" "万" "億" "兆")))))
寝る前にのぞいたらまた増えてる…ペースはやいですね。とりあえずぐっちゃぐっちゃと回答。うごいてるっぽい。 DOUKAKU-38> (number-to-kanji 1732050807568877) "千七百三十二兆五百八億七百五十六万八千八百七十七"
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
(defun number-to-kanji (num)
  (let ((digit   #("零" "一" "二" "三" "四" "五" "六" "七" "八" "九"))
        (subunit #("" "十" "百" "千"))
        (unit    #("" "万" "億" "兆")))
    (loop for i from 0
          initially (if (= num 0) (return (aref digit 0)))
          for (n m) = (multiple-value-list (floor num 10000)) ; 4 桁ずつ区切る
          until (and (= n 0) (= m 0))
          for value = (loop for j from 0 to 3 ; 位
                            for x = (mod (floor m (expt 10 j)) 10) ; 数字
                            when (/= x 0) ; 零千 零百 零十 対策
                            collect (format nil "~A~A" 
                                            ; 一千 一百 一十 対策
                                            (if (and (= x 1) (> j 0)) "" (aref digit x)) 
                                            (aref subunit j)))
           when value appending (cons (aref unit i) value) into result
           do (setf num n)
           finally (return (apply #'concatenate (cons 'string (reverse result)))))))
昔のコード、漢数字名があるかぎり表示可能 123456789012345678901234567890 =>十二穣三千四百五十六禾予七千八百九十外千二百三十四京五千六百七十八兆九千十二億三千四百五十六万七千八百九十 Data.UTF8 は手製のライブラリ http://www.sampou.org/cgi-bin/haskell.cgi?nobsun%3autf8
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
module Main where

import System.Environment
import Data.List
import qualified Data.UTF8 as U

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

toKan,toKan' :: String -> String
toKan  = concat . reverse .zipWith mkname base10000 
       . map (concat . reverse . conv4) . slices 4
toKan' = concat . reverse . zipWith mkname baseBig 
       . map toKan . slices 8

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'

slices :: Int -> [a] -> [[a]]
slices n = unfoldr phi
  where phi [] = Nothing
        phi xs = Just $ splitAt n xs

base1 = ["","一","二","三","四","五","六","七","八","九"]
base10 = ["","十","百","千"]
base10000 = ["","万","億","兆","京","外","禾予","穣","溝","澗","正","載","極"]
baseBig = ["恒河沙","阿僧祇","那由多","不可思議","無量大数"]

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

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
うへぇ 外じゃなくて 垓 ですね。タトしちゃった。
(n - (length s `mod` n)) `mod` n は (- length s) `mod n でよい?
1
2
3
4
5
-- 「なんとなくlength使いたくない Maybe大好き のオレが来ましたよ」的には
-- foo s = slices 4 $ padding 4 s
-- は
foo s = map (map (fromMaybe '0')) $ takeWhile (isJust.head)
  $ slices 4 $ map Just s ++ repeat Nothing
たぶん同じ時に書いたScheme版を発掘。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
(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 (大再帰 整数 大位列))))
  )
Squeak Smalltalk で。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
| 数 漢数字 |
数 := 1732050807568877.
漢数字 := ''.
#('' 万 億 兆 京) do: [:大位 |
    | 大位の数 |
    大位の数 := ''.
    #('' 十 百 千) do: [:小位 |
        | 小位の数 |
        小位の数 := #('' 一 二 三 四 五 六 七 八 九) at: 数 \\ 10 + 1.
        数 := 数 // 10.
        小位の数 ifNotEmpty: [
            (小位 notEmpty and: [小位の数 = '一']) ifTrue: [小位の数 := ''].
            大位の数 := 小位の数, 小位, 大位の数]].
    大位の数 ifNotEmpty: [漢数字 := 大位の数, 大位, 漢数字]].
^漢数字   "=> '千七百三十二兆五百八億七百五十六万八千八百七十七' "
M-x number-to-kanji <RET> 1732050807568877 <RET>
=>千七百三十二兆五百八億七百五十六万八千八百七十七
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
(require 'cl)
(defun number-to-kanji (str)
  (interactive "sNumber: ")
  (and (> (length str) 52) (error "52桁まで"))
  (labels ((nsplit (str)
             (labels ((rev (s) (concat (nreverse (mapcar #'identity s)))))
               (setq str (rev str))
               (let ((start 0) r)
                 (while (string-match ".\\{1,4\\}" str start)
                   (push (match-string 0 str) r)
                   (setq start (match-end 0)))
                 (mapcar #'rev r))))
           (kanji (str)
             (apply #'concat
                    (nreverse
                     (mapcar*
                      #'(lambda (i n)
                          (let ((s (nth (string-to-number n)
                                        '("〇" "一" "二" "三" "四" "五" "六" "七" "八" "九"))))
                            (and (> i 0) (string= s "一") (setq s ""))
                            (if (string= s "〇") ""
                                (concat s (nth i '("" "十" "百" "千"))))))
                      '(0 1 2 3) (nreverse (mapcar #'string str)))))))
    (let ((l (nreverse (mapcar #'kanji (nsplit str)))))
      (funcall
       (if current-prefix-arg #'insert #'message)
       (apply #'concat
              (nreverse
               (mapcar* #'(lambda (i s)
                            (concat s (if (string= s "") ""
                                          (nth i '("" "万" "億" "兆" "京" "垓" "(禾予)" "穣" "溝" "澗" "正" "載" "極")))))
                        (number-sequence 0 (- (length l) 1)) l)))))))