無駄と文化

実用的ブログ

毎月数時間を要していたスキャンデータ整理をOCRで自動化した

f:id:todays_mitsui:20161119134329j:plain


企業活動をするなかで見積書や請求書といった書類を発送するシーンは多いですよね。
私が勤める会社でもそういった書類をクライアントに郵送していますが、郵送する前の書類をスキャンしてスキャンデータを残しておく決まりになっています。

書類を作るのに必要なデータはすべて手元にあるものの、現物のスキャンデータがあれば安心なのも分かります。
書類に押したハンコを記録しておく意味もあるのかも知れません。


スキャンしたPDFの整理が負担に

しかし、毎月何百枚という書類のスキャンを取り発送するなかで、スキャンデータを整理する作業が負担になっていました。

スキャンを取る作業自体は書類の束をスキャナーに突っ込むだけなのですが、そうやって出来上がったPDFファイルはファイル名が 無機質な連番 になっています。
後で参照するときに目的のスキャンデータを探すことを考えると、一つひとつに適切なファイル名を付け直しておく必要があるわけです。

数百というファイルに適切な名前を付けるのは、単純ながら手間の掛かる作業です。
これまでは手作業でデータ整理をしていたようですが、スキャンデータのリネームだけで 毎月数時間を掛けていた ようです。
必要な作業とはいえあまり生産的とも思えませんし、今回は OCR 技術を使ってこのスキャンデータの整理を自動化してみました。

(※ OCR: Optical character recognition, 光学文字認識)


名前の付け方を確認しよう

最初にスキャンデータに付けるべき 適切なファイル名 について確認しました。

だいたいどの書類にも右上に「注文番号」と「発行した日付」が印字されています。
スキャンデータのPDFに付けるファイル名は注文番号と発行日付を単に並べたものでいいそうです。

f:id:todays_mitsui:20161119133353p:plain

(※画像は Image です)

例えば、注文番号が「123456」で発行日付が「2016年11月15日」であれば、ファイル名は「123456_20161115.pdf」といった具合ですね。

なるほど、

このファイル名のルール、自動化するにはなかなか都合がよさそうです。
一つずつ見ていきましょう。


1. 読み取る書類は全て弊社のフォーマット

書類毎にフォーマットがバラバラになってしまうと、求めている文字を読み取るだけでもかなり難しい課題になってしまいます。

しかし全てが自社フォーマットであればレイアウトも記載されている内容も揃っています。読み取る場所で悩むことはなさそうです。


2. 読み取り対処の文字は全て活字

手書き文字でもOCRで読めなくはないと思いますが、識字率はかなり低くなってしまうでしょう。
全てが活字で印字されているのは、正確に文字認識するうえでは理想的な条件です。


3. 読み取る文字は数字のみ

数字はたったの10種類しかなく、線もシンプルなのでもっとも文字認識しやすい対象だと云えます。
ひらがな・カタカナ・漢字混じりの文書をOCRで読もうとするとある程度の誤読を覚悟しなければいけませんが、対象が数字のみであることが事前に分かっていれば良い精度を出せそうです。

そんなわけで、充分な勝算があると踏んだ私はこのPDF整理自動化プロジェクトを(週末の連休を使って)粛々と進めることにしました。


自動化の方針

f:id:todays_mitsui:20161119125144p:plain

続いて自動化の方針を決定します。
自動化のプログラムはPythonで書くことにしました。具体的には以下のような手順です。

  1. PDFMiner で PDF ファイルから画像データの抜き出し
  2. 画像データ(生バイナリ)を PIL の Image オブジェクトに変換
  3. Tesseract で文字認識
  4. PDF ファイルを複製しつつリネーム

一つずつ解説します。


各工程の詳細

1. PDFMiner で PDF ファイルから画像データの抜き出し

今回は Tesseract という OCR ツールで画像の中の文字を読み取りますが。この Tesseract は JPEG や PNG などの形式しか受け付けてくれません。
PDF を直接読ませることができないので、事前準備として対応した形式に変換する必要がありました。


PDF をレンダリングして画像にする方法もあるようですが、下準備が煩雑で挫折...。
仕方なく今回は PDF を画像に変換するのではなく、PDF に埋め込まれた画像データを抽出する方法を採りました。

画像の抽出には PDFMiner という Python のライブラリを使います。
以下のコードを実行すると PDF に埋め込まれた全ての画像を取得することが出来ます。

from pdfminer.pdfinterp import PDFResourceManager
from pdfminer.pdfinterp import PDFPageInterpreter
from pdfminer.layout import LAParams, LTTextBox, LTTextLine, LTImage,  LTFigure
from pdfminer.converter import PDFPageAggregator


def extract_images(document):
    """PDF ドキュメントから画像形式のデータだけを抽出する"""

    # Create a PDF resource manager object that stores shared resources.
    rsrcmgr = PDFResourceManager()
    # Create a PDF device object.
    device = PDFPageAggregator(rsrcmgr, laparams=LAParams())
    # Create a PDF interpreter object.
    interpreter = PDFPageInterpreter(rsrcmgr, device)

    contents = []

    for page in PDFPage.create_pages(document):
        interpreter.process_page(page)
        layout = device.get_result()

        contents.extend(travarse(layout))

    return [to_pil_image(ltImage) for ltImage in contents]


def travarse(layout):
    """Layout オブジェクトを再帰的に走査して LTImage オブジェクトのみを list で返す"""

    images = []

    for obj in layout:
        if isinstance(obj, LTTextBox) or isinstance(obj, LTTextLine) or isinstance(obj, LTFigure):
            images.extend(travarse(obj))

        elif isinstance(obj, LTImage):
            images.append(obj)

    return images

ちなみに抽出されるデータの形式は生のバイナリ形式になります。


2. 画像データ(生バイナリ)を PIL の Image オブジェクトに変換

画像データを抽出できたのはいいものの、バイナリ形式のままでは何かと扱いにくいので Python で扱いやすい形式にします。 Python における代表的な画像処理ライブラリである Pillow の Image オブジェクト形式に変換しました。

import StringIO
from PIL import Image


def to_pil_image(ltImage):
    """Raw Binary を Image オブジェクトに変換"""

    buffer = StringIO.StringIO()
    buffer.write(ltImage.stream.get_rawdata())
    buffer.seek(0)
    return Image.open(buffer)

Image 形式に変換することで、元々埋め込まれていた画像データが JPG だったのか PNG だったのかを意識する必要がなくなります。
さらに、PIL の機能を使ってトリミングなどの補正処理もやりやすくなるので一石二鳥です。

標準的には Image オブジェクトはファイルストリームから生成することになっているので、 StringIO を使ってバイナリをもとにファイルストリームをエミュレートして Image オブジェクトを作ってあげます。


3. Tesseract で文字認識

いよいよ OCR に掛けて文字を読み取ってみましょう。

今回、OCR のツールとして Tesseract を利用しました。
Tesseract 自体は Python とは直接関係のない一般的な OCR ツールです。
160種類以上もの言語の学習済みデータが最初から付属している のが魅力で、中にはもちろん日本語用のものも含まれています。

事前に Tesseract をセットアップは済ませておく必要があります。
Tesseract を Python から呼び出すのには pyocr というラッパーモジュールを使いました。

試しに注文番号にあたる部分を読ませてみましょう。

f:id:todays_mitsui:20161119125229p:plain

はい、うまく読めているようですね。
先ほど述べたように、活字の数字のみであれば安定して文字認識できます。


続いて日付にあたる部分を読み取りましょう。

日付は「2016年11月15日」というような形式で書かれています。
最終的に正規表現で数字部分だけを抜き出すとはいえ、OCRに掛ける段階では「年」や「月」といった日本語の文字を読み取る必要があります。
Tesseract 標準の日本語用学習済みファイルを使って文字認識してみましょう。上手くいくでしょうか。

f:id:todays_mitsui:20161119125238p:plain

うーん、イマイチですね。
数字の 1] と読み違えています。全く似ていない (長音)に誤読してしまうのは学習に使った教師データに縦書きの文章が含まれているからでしょうか…。

とはいえ幸いなことにいま私が読み取ろうとしている文字は 0~9 と「年月日」の13文字だけです。それ以外の文字はありえない事が分かっているので、誤読の訂正も簡単なのです。
OCRに掛けた結果に ] が含まれれば 1 の誤読であろうと分かります。 Z が含まれれば恐らく 2 の誤読ですね。

というわけで、誤読されていそうな文字を一つひとつ補正する処理を挟みます。

REPLACE_PAIR = (
    (u']', u'1'), (u'}', u'1'), (u'ー', u'1'),
    (u'仔', u'年'), (u'El', u'日'), (u'E|', u'日'),
    (u'E', u'日'), (u'口', u'日'), (u'曰', u'日'),
    (u'Z', u'2'), (u'O', u'0'), (u'〇', u'0'),
    (u'I', u'1'), (u'l', u'1'),
)

# よくある誤読をヒューリスティックに訂正
for before, after in REPLACE_PAIR:
    txt = txt.replace(before, after)

# 空白を削除
txt = re.sub(r'\s+', '', txt)

このような工夫の結果、テスト用に用意したスキャンデータ20個では何とか正答率100%を達成できました。


4. PDF ファイルを複製しつつリネーム

注文番号と日付を読み取ることさえ出来れば、最後のリネームはとても簡単です。
shutil モジュールに含まれる copyfile 関数で複製しつつファイル名を変えてあげます。

import shutil


after = '{0[ordernum]}_{1[year]:0>4}{1[month]:0>2}{1[day]:0>2}.pdf'.format(ordernum, date)
shutil.copyfile(before, 'AFTER/'+after)

このような処理をフォルダに用意したスキャンデータの一つひとつに適用していけば、ファイル整理の自動化は達成されます。

f:id:todays_mitsui:20161119130455p:plain

いい感じです。


配布する

これにて目的は達成出来ましたが、最後にこのプログラムを自分以外の人にも配布することについて検討します。
実務で必要な人が手軽に使えるようにとかんがえると、やはり Windows の実行形式である EXE ファイルを作って配布するべきでしょうね。

Python で書いたプログラムにおいては、Pinstaller というツールを使う事で必要なライブラリなどを自動で取り込んだ EXE ファイルを吐き出してくれます。

$ pyinstaller main.py --onefile --clean

実行するコンピュータに Tesseract が別途セットアップされている必要はあるものの、EXE ファイルを配りさえすれば使ってもらえるのはとても便利です。
Pyinstler のインストールは pip コマンドひとつで済むので使い始めるのも楽でした。


まとめ

というわけで、PDFファイルから画像を抽出し、文字を読み取ってスキャンデータをリネームするところまで一通りの処理を自動化してみました。
Python は様々な分野で必要になる基本的ツールが何かしら揃っていて成熟しているので便利ですね。これはいいものです。

今回書いたコードの一式は GitHub に置いています。

github.com


私からは以上です。

Lazy_K で九九表のすべてのマスの和

前回の記事で 九九表のすべてのマスの和を求める問題 をいろいろな言語で書きました。
記事の最後でそのうち Lazy_K でも書きます的な事を宣言しておいたんですが、この度そのコードが完成しましたのでご報告します。


Lazy_K って何?

純粋関数型言語の1つです、位置づけとしては ジョーク言語, 難解言語, ホビー言語 などに分類されるんじゃないでしょうか。

特徴としては言語に組み込まれたシンボルの少なさです。
Unlambda記法であれば、s, k, i, ` の4つのシンボルだけでチューリング完全な表現力を持っています。
Lazy_K の言語的なおもしろさは以下の記事を読んでいただければ(分かる人には)分かると思います。


マイナーな言語ですのでやってみようと思うと実行環境の準備からしなければいけないんですが、幸いなことにブラウザで実行できる REPL がありました。
上の窓に「プログラムの本体」、下の窓に「プログラムへの入力」を入力して実行することができます。


コードを書く

九九表のすべてのマスの和を求めるにあたってアルゴリズムの大筋は前回 JavaScript で書いたものと同じです。
引用します。

// range(1, 9) の代わり
const ns = [...Array(9).keys()].map(i => i+1);

// flatten() の代わり
const flatten = arr => [].concat(...arr);

// sum() の代わり
const sum = ns => ns.reduce((i, j) => i+j, 0);

let result = sum(flatten(ns.map((i, _, ms) => ms.map(j => i*j))));
console.log(result);
// => 2025

数列を組み立てて、直積を求め、flatten して、sum を取る。これです。


ちなみに前回、こんなことを愚痴っていましたが、

Array.range()Array.flatten()Set.product()Math.sum() も無いとかさすがにツラいです。

はい、実は Lazy_K にもこれらの関数はありません。
それどころか自然数も積も和も if then else などの制御構文も一切ありません。唯一あるのは関数適用だけです。

なので何もかも自分で作らないといけませんね。まいったまいった。


下準備

いくら Lazy_K がチューリング完全とはいえ、そのままでは人間がプログラムを書くのはツラすぎます。せめて λ抽象 があれば、という感じです。
なので Lazy_K に λ抽象 のシンタックスを追加して、それを Lazy_K のコードに変換するトランスレータを書きました。

github.com

これです。

このツールの使い方については Qiita に記事を上げておきました。

qiita.com


正直、このツールを作るのにもガッツリ Haskell 書いてるので下準備どころではないですが。


今度こそコードを書く

さて必要なパーツを淡々と作っていきます。
基本的には λ抽象 を含むコードを書いていきますが、純粋な Lazy_K のコードも適宜 見せていきます。

真理値

TRUE  = ^xy.x
# => k

FALSE = ^xy.y
# => `ki

IF    = ^BOOL THEN ELSE.``BOOL THEN ELSE
# => i


NOT = ^x.``xFALSE TRUE
# => ``s``si`k`ki`kk

AND = ^xy.``xyFALSE
# => ``ss`k`k`ki

OR  = ^xy.``xTRUEy
# => ``si`kk

ペア・リスト

複数のデータを保持するのには連鎖リストを使います。

CONS = ^xyf.``fxy
# => ``s``s`ks``s`kk``s`ks``s`k`sik`kk

CAR  = ^x.`xTRUE
# => ``si`kk

CDR  = ^x.`xFALSE
# => ``si`k`ki


NULL   = FALSE
# => `ki

ISNULL = ^x.``x^_ _ _.FALSE TRUE
# => ``s``si`k`k`k`k`ki`kk

不動点コンビネータ

再帰的関数を定義するために無名再帰が必要です。無名再帰のためには不動点コンビネータですよね。
今回はハスケル・カリー先生のYコンビネータを使います。

Y = ^f.`^x.`f`xx^x.`f`xx
# => ``s``s``s`ksk`k``sii``s``s`ksk`k``sii

自然数

自然数はチャーチ数として表現します。

0   = ^fx.x
# => `ki

1   = ^fx.`fx
# => i

2   = ^fx.`f`fx
# => ``s``s`kski

3   = ^fx.`f`f`fx
# => ``s``s`ksk``s``s`kski

4   = ```sii2
# => ```sii``s``s`kski

256 = ```sii4
# => ```sii```sii``s``s`kski

各種演算も定義しておかなければいけません。
DIV と余剰 MOD の定義には再帰を使います。先ほどのYコンビネータの出番です。

ISZERO = ^n.``n^_.FALSE TRUE
# => ``s``si`k`k`ki`kk


GTE = ^mn.`ISZERO``SUBnm           # 大なりイコール
# => ``s`k`s`k``s``si`k`k`ki`kk``s`k`s``s`k`s``si`k``s``s`ks``s`k`s`ks``s``s`ks`
     `s`k`s`ks``s`k`s`kk``s``s`ksk`k``s`k`s`k`si``s`k`s`kk``s`k`sik`k`kk`k`k`kikk

GT  = ^mn.``AND `ISZERO``SUBnm `NOT `ISZERO``SUBmn
                                   # 大なり
# => ``s``s`ks``s`k`s`k``ss`k`k`ki``s`k`s`k``s``si`k`k`ki`kk``s`k`s``s`k`s``si`k
     ``s``s`ks``s`k`s`ks``s``s`ks``s`k`s`ks``s`k`s`kk``s``s`ksk`k``s`k`s`k`si``s
     `k`s`kk``s`k`sik`k`kk`k`k`kikk``s`k`s`k``s``si`k`ki`kk``s`k`s`k``s``si`k`k`
     ki`kk``s`k`s``si`k``s``s`ks``s`k`s`ks``s``s`ks``s`k`s`ks``s`k`s`kk``s``s`ks
     k`k``s`k`s`k`si``s`k`s`kk``s`k`sik`k`kk`k`k`kik

LTE = ^mn.`ISZERO``SUBmn           # 小なりイコール
# => ``s`k`s`k``s``si`k`k`ki`kk``s`k`s``si`k``s``s`ks``s`k`s`ks``s``s`ks``s`k`s`
     ks``s`k`s`kk``s``s`ksk`k``s`k`s`k`si``s`k`s`kk``s`k`sik`k`kk`k`k`kik

LT  = ^mn.``AND `ISZERO``SUBmn `NOT `ISZERO``SUBnm
                                   # 小なり
# => ``s``s`ks``s`k`s`k``ss`k`k`ki``s`k`s`k``s``si`k`k`ki`kk``s`k`s``si`k``s``s`
     ks``s`k`s`ks``s``s`ks``s`k`s`ks``s`k`s`kk``s``s`ksk`k``s`k`s`k`si``s`k`s`kk
     ``s`k`sik`k`kk`k`k`kik``s`k`s`k``s``si`k`ki`kk``s`k`s`k``s``si`k`k`ki`kk``s
     `k`s``s`k`s``si`k``s``s`ks``s`k`s`ks``s``s`ks``s`k`s`ks``s`k`s`kk``s``s`ksk
     `k``s`k`s`k`si``s`k`s`kk``s`k`sik`k`kk`k`k`kikk

EQ = ^mn.``AND``GTEmn``LTEmn
                                   # イコール
# => ``s``s`ks``s`k`s`k``ss`k`k`ki``s`k`s`k``s``si`k`k`ki`kk``s`k`s``s`k`s``si`k
     ``s``s`ks``s`k`s`ks``s``s`ks``s`k`s`ks``s`k`s`kk``s``s`ksk`k``s`k`s`k`si``s
     `k`s`kk``s`k`sik`k`kk`k`k`kikk``s`k`s`k``s``si`k`k`ki`kk``s`k`s``si`k``s``s
     `ks``s`k`s`ks``s``s`ks``s`k`s`ks``s`k`s`kk``s``s`ksk`k``s`k`s`k`si``s`k`s`k
     k``s`k`sik`k`kk`k`k`kik


SUCC = ^n.^fx.`f``nfx             # 後者関数
# => `s``s`ksk


PLUS = ^mn.^fx.``mf``nfx          # 和
# => ``s`ks``s`k`s`ks`s`kk

MULT = ^mn.^f.`m`nf               # 積
# => ``s`ksk

POW  = ^mn.`nm                    # 累乗
# => ``s`k`sik


PRED = ^nfx.```n^gh.`h`gf^u.x^u.u # 前者関数
# => ``s``s`ks``s`k`s`ks``s``s`ks``s`k`s`ks``s`k`s`kk``s``s`ksk`k``s`k`s`k`si``s
     `k`s`kk``s`k`sik`k`kk`k`k`ki

SUB  = ^mn.``nPREDm               # 差
# => ``s`k`s``si`k``s``s`ks``s`k`s`ks``s``s`ks``s`k`s`ks``s`k`s`kk``s``s`ksk`k``
     s`k`s`k`si``s`k`s`kk``s`k`sik`k`kk`k`k`kik

DIV = `Y^DIV.^mn.```IF``LTmn 0 `SUCC ``DIV ``SUBmn n
                                  # 商
# => ```s``s``s`ksk`k``sii``s``s`ksk`k``sii``s`k`s``s`ks``s``s`ks``s`k`s`ki``s``
     s`ks``s`k`s`k``ss`k`k`ki``s`k`s`k``s``si`k`k`ki`kk``s`k`s``si`k``s``s`ks``s
     `k`s`ks``s``s`ks``s`k`s`ks``s`k`s`kk``s``s`ksk`k``s`k`s`k`si``s`k`s`kk``s`k
     `sik`k`kk`k`k`kik``s`k`s`k``s``si`k`ki`kk``s`k`s`k``s``si`k`k`ki`kk``s`k`s`
     `s`k`s``si`k``s``s`ks``s`k`s`ks``s``s`ks``s`k`s`ks``s`k`s`kk``s``s`ksk`k``s
     `k`s`k`si``s`k`s`kk``s`k`sik`k`kk`k`k`kikk`k`k`ki``s`k`s`k`s`k`s``s`ksk``s`
     `s`ks``s`k`s`ks``s``s`ks``s`kk``s`ksk`k``s`k`s``si`k``s``s`ks``s`k`s`ks``s`
     `s`ks``s`k`s`ks``s`k`s`kk``s``s`ksk`k``s`k`s`k`si``s`k`s`kk``s`k`sik`k`kk`k
     `k`kik`k`ki

MOD = `Y^MOD.^mn.```IF``LTmn m ``MOD ``SUBmn n
                                  # 余剰
# => ```s``s``s`ksk`k``sii``s``s`ksk`k``sii``s`k`s``s`ks``s``s`ks``s`k`s`ki``s``
     s`ks``s`k`s`k``ss`k`k`ki``s`k`s`k``s``si`k`k`ki`kk``s`k`s``si`k``s``s`ks``s
     `k`s`ks``s``s`ks``s`k`s`ks``s`k`s`kk``s``s`ksk`k``s`k`s`k`si``s`k`s`kk``s`k
     `sik`k`kk`k`k`kik``s`k`s`k``s``si`k`ki`kk``s`k`s`k``s``si`k`k`ki`kk``s`k`s`
     `s`k`s``si`k``s``s`ks``s`k`s`ks``s``s`ks``s`k`s`ks``s`k`s`kk``s``s`ksk`k``s
     `k`s`k`si``s`k`s`kk``s`k`sik`k`kk`k`k`kikkk``s``s`ks``s`k`s`ks``s``s`ks``s`
     kk``s`ksk`k``s`k`s``si`k``s``s`ks``s`k`s`ks``s``s`ks``s`k`s`ks``s`k`s`kk``s
     ``s`ksk`k``s`k`s`k`si``s`k`s`kk``s`k`sik`k`kk`k`k`kik`k`ki

自然数の表示

自然数を扱えるようになりましたが、文字列に変換しなければ表示ができません。
自然数を文字列に変換する TOSTRING 関数を定義します。
出力ストリームの終端を表す EOF という値も定義しておきます。

TOSTRING_ = `Y^TOSTRING_.^n.``CONS ``PLUS 48 ``MODn10 ```IF ``LTEn9 NULL `TOSTRING_``DIVn10
# => ```s``s``s`ksk`k``sii``s``s`ksk`k``sii``s`k`s``s`k``s``s`ks``s`kk``s`ks``s`
     k`sik`kk``s`k```s`ks``s`k`s`ks`s`kk````s`ks``s`k`s`ks`s`kk````s`ksk```sii``
     s``s`kski````s`ksk``s``s`kski``s``s`ksk``s``s`ksk``s``s`ksk``s``s`kski```s`
     `s`ksk``s``s`kski``s``s`kski``s```s``s``s`ksk`k``sii``s``s`ksk`k``sii``s`k`
     s``s`ks``s``s`ks``s`k`s`ki``s``s`ks``s`k`s`k``ss`k`k`ki``s`k`s`k``s``si`k`k
     `ki`kk``s`k`s``si`k``s``s`ks``s`k`s`ks``s``s`ks``s`k`s`ks``s`k`s`kk``s``s`k
     sk`k``s`k`s`k`si``s`k`s`kk``s`k`sik`k`kk`k`k`kik``s`k`s`k``s``si`k`ki`kk``s
     `k`s`k``s``si`k`k`ki`kk``s`k`s``s`k`s``si`k``s``s`ks``s`k`s`ks``s``s`ks``s`
     k`s`ks``s`k`s`kk``s``s`ksk`k``s`k`s`k`si``s`k`s`kk``s`k`sik`k`kk`k`k`kikkk`
     `s``s`ks``s`k`s`ks``s``s`ks``s`kk``s`ksk`k``s`k`s``si`k``s``s`ks``s`k`s`ks`
     `s``s`ks``s`k`s`ks``s`k`s`kk``s``s`ksk`k``s`k`s`k`si``s`k`s`kk``s`k`sik`k`k
     k`k`k`kik`k`ki`k````s`ksk``s``s`kski``s``s`ksk``s``s`ksk``s``s`ksk``s``s`ks
     ki``s`k`s``s``s`ki``s``s`k`s`k``s``si`k`k`ki`kk``s`k`s``si`k``s``s`ks``s`k`
     s`ks``s``s`ks``s`k`s`ks``s`k`s`kk``s``s`ksk`k``s`k`s`k`si``s`k`s`kk``s`k`si
     k`k`kk`k`k`kik`k```s``s`kski``s``s`ksk``s``s`kski`k`ki``s``s`ksk`k``s```s``
     s``s`ksk`k``sii``s``s`ksk`k``sii``s`k`s``s`ks``s``s`ks``s`k`s`ki``s``s`ks``
     s`k`s`k``ss`k`k`ki``s`k`s`k``s``si`k`k`ki`kk``s`k`s``si`k``s``s`ks``s`k`s`k
     s``s``s`ks``s`k`s`ks``s`k`s`kk``s``s`ksk`k``s`k`s`k`si``s`k`s`kk``s`k`sik`k
     `kk`k`k`kik``s`k`s`k``s``si`k`ki`kk``s`k`s`k``s``si`k`k`ki`kk``s`k`s``s`k`s
     ``si`k``s``s`ks``s`k`s`ks``s``s`ks``s`k`s`ks``s`k`s`kk``s``s`ksk`k``s`k`s`k
     `si``s`k`s`kk``s`k`sik`k`kk`k`k`kikk`k`k`ki``s`k`s`k`s`k`s``s`ksk``s``s`ks`
     `s`k`s`ks``s``s`ks``s`kk``s`ksk`k``s`k`s``si`k``s``s`ks``s`k`s`ks``s``s`ks`
     `s`k`s`ks``s`k`s`kk``s``s`ksk`k``s`k`s`k`si``s`k`s`kk``s`k`sik`k`kk`k`k`kik
     `k`ki`k````s`ksk``s``s`kski``s``s`ksk``s``s`ksk``s``s`ksk``s``s`kski

TOSTRING = ^n.``REVERSE_ EOF `TOSTRING_ n
# => ``s`k````s``s``s`ksk`k``sii``s``s`ksk`k``sii``s`k`s``s`ks``s`k`s``s`ki``s``
     si`k`k`k`k`ki`kkk``s``s`ks``s`k`s`ks``s``s`ks``s`kk``s`ksk`k``s`k`s``s`k``s
     ``s`ks``s`kk``s`ks``s`k`sik`kk``si`kkk`k`k``si`k`ki```sii```sii``s``s`kski`
     ``s``s``s`ksk`k``sii``s``s`ksk`k``sii``s`k`s``s`k``s``s`ks``s`kk``s`ks``s`k
     `sik`kk``s`k```s`ks``s`k`s`ks`s`kk````s`ks``s`k`s`ks`s`kk````s`ksk```sii``s
     ``s`kski````s`ksk``s``s`kski``s``s`ksk``s``s`ksk``s``s`ksk``s``s`kski```s``
     s`ksk``s``s`kski``s``s`kski``s```s``s``s`ksk`k``sii``s``s`ksk`k``sii``s`k`s
     ``s`ks``s``s`ks``s`k`s`ki``s``s`ks``s`k`s`k``ss`k`k`ki``s`k`s`k``s``si`k`k`
     ki`kk``s`k`s``si`k``s``s`ks``s`k`s`ks``s``s`ks``s`k`s`ks``s`k`s`kk``s``s`ks
     k`k``s`k`s`k`si``s`k`s`kk``s`k`sik`k`kk`k`k`kik``s`k`s`k``s``si`k`ki`kk``s`
     k`s`k``s``si`k`k`ki`kk``s`k`s``s`k`s``si`k``s``s`ks``s`k`s`ks``s``s`ks``s`k
     `s`ks``s`k`s`kk``s``s`ksk`k``s`k`s`k`si``s`k`s`kk``s`k`sik`k`kk`k`k`kikkk``
     s``s`ks``s`k`s`ks``s``s`ks``s`kk``s`ksk`k``s`k`s``si`k``s``s`ks``s`k`s`ks``
     s``s`ks``s`k`s`ks``s`k`s`kk``s``s`ksk`k``s`k`s`k`si``s`k`s`kk``s`k`sik`k`kk
     `k`k`kik`k`ki`k````s`ksk``s``s`kski``s``s`ksk``s``s`ksk``s``s`ksk``s``s`ksk
     i``s`k`s``s``s`ki``s``s`k`s`k``s``si`k`k`ki`kk``s`k`s``si`k``s``s`ks``s`k`s
     `ks``s``s`ks``s`k`s`ks``s`k`s`kk``s``s`ksk`k``s`k`s`k`si``s`k`s`kk``s`k`sik
     `k`kk`k`k`kik`k```s``s`kski``s``s`ksk``s``s`kski`k`ki``s``s`ksk`k``s```s``s
     ``s`ksk`k``sii``s``s`ksk`k``sii``s`k`s``s`ks``s``s`ks``s`k`s`ki``s``s`ks``s
     `k`s`k``ss`k`k`ki``s`k`s`k``s``si`k`k`ki`kk``s`k`s``si`k``s``s`ks``s`k`s`ks
     ``s``s`ks``s`k`s`ks``s`k`s`kk``s``s`ksk`k``s`k`s`k`si``s`k`s`kk``s`k`sik`k`
     kk`k`k`kik``s`k`s`k``s``si`k`ki`kk``s`k`s`k``s``si`k`k`ki`kk``s`k`s``s`k`s`
     `si`k``s``s`ks``s`k`s`ks``s``s`ks``s`k`s`ks``s`k`s`kk``s``s`ksk`k``s`k`s`k`
     si``s`k`s`kk``s`k`sik`k`kk`k`k`kikk`k`k`ki``s`k`s`k`s`k`s``s`ksk``s``s`ks``
     s`k`s`ks``s``s`ks``s`kk``s`ksk`k``s`k`s``si`k``s``s`ks``s`k`s`ks``s``s`ks``
     s`k`s`ks``s`k`s`kk``s``s`ksk`k``s`k`s`k`si``s`k`s`kk``s`k`sik`k`kk`k`k`kik`
     k`ki`k````s`ksk``s``s`kski``s``s`ksk``s``s`ksk``s``s`ksk``s``s`kski

EOF = 256
# => ```sii```sii``s``s`kski

ISEOF = ^x.``EQx256
# => ``s``s``s`ks``s`k`s`k``ss`k`k`ki``s`k`s`k``s``si`k`k`ki`kk``s`k`s``s`k`s``s
     i`k``s``s`ks``s`k`s`ks``s``s`ks``s`k`s`ks``s`k`s`kk``s``s`ksk`k``s`k`s`k`si
     ``s`k`s`kk``s`k`sik`k`kk`k`k`kikk``s`k`s`k``s``si`k`k`ki`kk``s`k`s``si`k``s
     ``s`ks``s`k`s`ks``s``s`ks``s`k`s`ks``s`k`s`kk``s``s`ksk`k``s`k`s`k`si``s`k`
     s`kk``s`k`sik`k`kk`k`k`kik`k```sii```sii``s``s`kski

リスト操作の高階関数

お馴染みの map や append, reverse などを定義します。もう少しです。

MAP = `Y^MAP.^f XS.```IF `ISNULL XS NULL ``CONS `f`CAR XS ``MAPf`CDR XS
# => ```s``s``s`ksk`k``sii``s``s`ksk`k``sii``s`k`s`k`s``s``s`ki``s``si`k`k`k`k`k
     i`kk`k`ki``s`k`s``s`ks``s`k`s`k``s``s`ks``s`kk``s`ks``s`k`sik`kk``s``s`ksk`
     k``si`kk``s``s`ks``s`k`s`ks`s`kk`k`k``si`k`ki

APPEND = `Y^APPEND.^XS YS.```IF `ISNULL XS YS ``CONS `CAR XS ``APPEND `CDR XS YS
# => ```s``s``s`ksk`k``sii``s``s`ksk`k``sii``s`k`s``s`ks``s`ki``s``si`k`k`k`k`ki
     `kk``s`k`s``s`ks``s`kk``s`k``s``s`ks``s`kk``s`ks``s`k`sik`kk``si`kk``s``s`k
     sk`k``si`k`ki

REVERSE_ = `Y^REVERSE_.^ACC XS.```IF `ISNULL XS ACC ``REVERSE_ ``CONS `CAR XS ACC `CDR XS
# => ```s``s``s`ksk`k``sii``s``s`ksk`k``sii``s`k`s``s`ks``s`k`s``s`ki``s``si`k`k
     `k`k`ki`kkk``s``s`ks``s`k`s`ks``s``s`ks``s`kk``s`ksk`k``s`k`s``s`k``s``s`ks
     ``s`kk``s`ks``s`k`sik`kk``si`kkk`k`k``si`k`ki

REVERSE = `REVERSE_ NULL
# => ````s``s``s`ksk`k``sii``s``s`ksk`k``sii``s`k`s``s`ks``s`k`s``s`ki``s``si`k`
     k`k`k`ki`kkk``s``s`ks``s`k`s`ks``s``s`ks``s`kk``s`ksk`k``s`k`s``s`k``s``s`k
     s``s`kk``s`ks``s`k`sik`kk``si`kkk`k`k``si`k`ki`ki

CONCAT = `Y^CONCAT.^XSS.```IF `ISNULL XSS NULL ``APPEND `CAR XSS `CONCAT `CDR XSS
# => ```s``s``s`ksk`k``sii``s``s`ksk`k``sii``s`k`s``s``s`ki``s``si`k`k`k`k`ki`kk
     `k`ki``s`k`s``s`k```s``s``s`ksk`k``sii``s``s`ksk`k``sii``s`k`s``s`ks``s`ki`
     `s``si`k`k`k`k`ki`kk``s`k`s``s`ks``s`kk``s`k``s``s`ks``s`kk``s`ks``s`k`sik`
     kk``si`kk``s``s`ksk`k``si`k`ki``si`kk``s``s`ksk`k``si`k`ki

RANGE = `Y^RANGE.^n.``CONS n ```IF `ISZEROn NULL `RANGE`PREDn
# => ```s``s``s`ksk`k``sii``s``s`ksk`k``sii``s`k`s``s``s`ks``s`kk``s`ks``s`k`sik
     `kk``s`k`s``s``s`ki``s``si`k`k`ki`kk`k`ki``s``s`ksk`k``s``s`ks``s`k`s`ks``s
     ``s`ks``s`k`s`ks``s`k`s`kk``s``s`ksk`k``s`k`s`k`si``s`k`s`kk``s`k`sik`k`kk`
     k`k`ki

SUM = `Y^SUM.^NS.```IF `ISNULL NS 0 ``PLUS `CAR NS `SUM `CDR NS
# => ```s``s``s`ksk`k``sii``s``s`ksk`k``sii``s`k`s``s``s`ki``s``si`k`k`k`k`ki`kk
     `k`ki``s`k`s``s`k``s`ks``s`k`s`ks`s`kk``si`kk``s``s`ksk`k``si`k`ki

九九表のすべてのマスの和

お待たせしました、やっと本題です。

NUMS = ``MAP SUCC `RANGE 8
# => `````s``s``s`ksk`k``sii``s``s`ksk`k``sii``s`k`s`k`s``s``s`ki``s``si`k`k`k`k
     `ki`kk`k`ki``s`k`s``s`ks``s`k`s`k``s``s`ks``s`kk``s`ks``s`k`sik`kk``s``s`ks
     k`k``si`kk``s``s`ks``s`k`s`ks`s`kk`k`k``si`k`ki`s``s`ksk````s``s``s`ksk`k``
     sii``s``s`ksk`k``sii``s`k`s``s``s`ks``s`kk``s`ks``s`k`sik`kk``s`k`s``s``s`k
     i``s``si`k`k`ki`kk`k`ki``s``s`ksk`k``s``s`ks``s`k`s`ks``s``s`ks``s`k`s`ks``
     s`k`s`kk``s``s`ksk`k``s`k`s`k`si``s`k`s`kk``s`k`sik`k`kk`k`k`ki```s``s`ksk`
     `s``s`kski``s``s`kski

KUKU = `SUM `CONCAT ``MAP ^n.``MAP ^m.``MULT n m NUMS NUMS
# => ````s``s``s`ksk`k``sii``s``s`ksk`k``sii``s`k`s``s``s`ki``s``si`k`k`k`k`ki`k
     k`k`ki``s`k`s``s`k``s`ks``s`k`s`ks`s`kk``si`kk``s``s`ksk`k``si`k`ki````s``s
     ``s`ksk`k``sii``s``s`ksk`k``sii``s`k`s``s``s`ki``s``si`k`k`k`k`ki`kk`k`ki``
     s`k`s``s`k```s``s``s`ksk`k``sii``s``s`ksk`k``sii``s`k`s``s`ks``s`ki``s``si`
     k`k`k`k`ki`kk``s`k`s``s`ks``s`kk``s`k``s``s`ks``s`kk``s`ks``s`k`sik`kk``si`
     kk``s``s`ksk`k``si`k`ki``si`kk``s``s`ksk`k``si`k`ki`````s``s``s`ksk`k``sii`
     `s``s`ksk`k``sii``s`k`s`k`s``s``s`ki``s``si`k`k`k`k`ki`kk`k`ki``s`k`s``s`ks
     ``s`k`s`k``s``s`ks``s`kk``s`ks``s`k`sik`kk``s``s`ksk`k``si`kk``s``s`ks``s`k
     `s`ks`s`kk`k`k``si`k`ki``s``s`k```s``s``s`ksk`k``sii``s``s`ksk`k``sii``s`k`
     s`k`s``s``s`ki``s``si`k`k`k`k`ki`kk`k`ki``s`k`s``s`ks``s`k`s`k``s``s`ks``s`
     kk``s`ks``s`k`sik`kk``s``s`ksk`k``si`kk``s``s`ks``s`k`s`ks`s`kk`k`k``si`k`k
     i``s`ksk`k`````s``s``s`ksk`k``sii``s``s`ksk`k``sii``s`k`s`k`s``s``s`ki``s``
     si`k`k`k`k`ki`kk`k`ki``s`k`s``s`ks``s`k`s`k``s``s`ks``s`kk``s`ks``s`k`sik`k
     k``s``s`ksk`k``si`kk``s``s`ks``s`k`s`ks`s`kk`k`k``si`k`ki`s``s`ksk````s``s`
     `s`ksk`k``sii``s``s`ksk`k``sii``s`k`s``s``s`ks``s`kk``s`ks``s`k`sik`kk``s`k
     `s``s``s`ki``s``si`k`k`ki`kk`k`ki``s``s`ksk`k``s``s`ks``s`k`s`ks``s``s`ks``
     s`k`s`ks``s`k`s`kk``s``s`ksk`k``s`k`s`k`si``s`k`s`kk``s`k`sik`k`kk`k`k`ki``
     `s``s`ksk``s``s`kski``s``s`kski`````s``s``s`ksk`k``sii``s``s`ksk`k``sii``s`
     k`s`k`s``s``s`ki``s``si`k`k`k`k`ki`kk`k`ki``s`k`s``s`ks``s`k`s`k``s``s`ks``
     s`kk``s`ks``s`k`sik`kk``s``s`ksk`k``si`kk``s``s`ks``s`k`s`ks`s`kk`k`k``si`k
     `ki`s``s`ksk````s``s``s`ksk`k``sii``s``s`ksk`k``sii``s`k`s``s``s`ks``s`kk``
     s`ks``s`k`sik`kk``s`k`s``s``s`ki``s``si`k`k`ki`kk`k`ki``s``s`ksk`k``s``s`ks
     ``s`k`s`ks``s``s`ks``s`k`s`ks``s`k`s`kk``s``s`ksk`k``s`k`s`k`si``s`k`s`kk``
     s`k`sik`k`kk`k`k`ki```s``s`ksk``s``s`kski``s``s`kski

MAIN = ^_.``APPEND `TOSTRING KUKU EOF
# => `k`````s``s``s`ksk`k``sii``s``s`ksk`k``sii``s`k`s``s`ks``s`ki``s``si`k`k`k`
     k`ki`kk``s`k`s``s`ks``s`kk``s`k``s``s`ks``s`kk``s`ks``s`k`sik`kk``si`kk``s`
     `s`ksk`k``si`k`ki```s`k````s``s``s`ksk`k``sii``s``s`ksk`k``sii``s`k`s``s`ks
     ``s`k`s``s`ki``s``si`k`k`k`k`ki`kkk``s``s`ks``s`k`s`ks``s``s`ks``s`kk``s`ks
     k`k``s`k`s``s`k``s``s`ks``s`kk``s`ks``s`k`sik`kk``si`kkk`k`k``si`k`ki```sii
     ```sii``s``s`kski```s``s``s`ksk`k``sii``s``s`ksk`k``sii``s`k`s``s`k``s``s`k
     s``s`kk``s`ks``s`k`sik`kk``s`k```s`ks``s`k`s`ks`s`kk````s`ks``s`k`s`ks`s`kk
     ````s`ksk```sii``s``s`kski````s`ksk``s``s`kski``s``s`ksk``s``s`ksk``s``s`ks
     k``s``s`kski```s``s`ksk``s``s`kski``s``s`kski``s```s``s``s`ksk`k``sii``s``s
     `ksk`k``sii``s`k`s``s`ks``s``s`ks``s`k`s`ki``s``s`ks``s`k`s`k``ss`k`k`ki``s
     `k`s`k``s``si`k`k`ki`kk``s`k`s``si`k``s``s`ks``s`k`s`ks``s``s`ks``s`k`s`ks`
     `s`k`s`kk``s``s`ksk`k``s`k`s`k`si``s`k`s`kk``s`k`sik`k`kk`k`k`kik``s`k`s`k`
     `s``si`k`ki`kk``s`k`s`k``s``si`k`k`ki`kk``s`k`s``s`k`s``si`k``s``s`ks``s`k`
     s`ks``s``s`ks``s`k`s`ks``s`k`s`kk``s``s`ksk`k``s`k`s`k`si``s`k`s`kk``s`k`si
     k`k`kk`k`k`kikkk``s``s`ks``s`k`s`ks``s``s`ks``s`kk``s`ksk`k``s`k`s``si`k``s
     ``s`ks``s`k`s`ks``s``s`ks``s`k`s`ks``s`k`s`kk``s``s`ksk`k``s`k`s`k`si``s`k`
     s`kk``s`k`sik`k`kk`k`k`kik`k`ki`k````s`ksk``s``s`kski``s``s`ksk``s``s`ksk``
     s``s`ksk``s``s`kski``s`k`s``s``s`ki``s``s`k`s`k``s``si`k`k`ki`kk``s`k`s``si
     `k``s``s`ks``s`k`s`ks``s``s`ks``s`k`s`ks``s`k`s`kk``s``s`ksk`k``s`k`s`k`si`
     `s`k`s`kk``s`k`sik`k`kk`k`k`kik`k```s``s`kski``s``s`ksk``s``s`kski`k`ki``s`
     `s`ksk`k``s```s``s``s`ksk`k``sii``s``s`ksk`k``sii``s`k`s``s`ks``s``s`ks``s`
     k`s`ki``s``s`ks``s`k`s`k``ss`k`k`ki``s`k`s`k``s``si`k`k`ki`kk``s`k`s``si`k`
     `s``s`ks``s`k`s`ks``s``s`ks``s`k`s`ks``s`k`s`kk``s``s`ksk`k``s`k`s`k`si``s`
     k`s`kk``s`k`sik`k`kk`k`k`kik``s`k`s`k``s``si`k`ki`kk``s`k`s`k``s``si`k`k`ki
     `kk``s`k`s``s`k`s``si`k``s``s`ks``s`k`s`ks``s``s`ks``s`k`s`ks``s`k`s`kk``s`
     `s`ksk`k``s`k`s`k`si``s`k`s`kk``s`k`sik`k`kk`k`k`kikk`k`k`ki``s`k`s`k`s`k`s
     ``s`ksk``s``s`ks``s`k`s`ks``s``s`ks``s`kk``s`ksk`k``s`k`s``si`k``s``s`ks``s
     `k`s`ks``s``s`ks``s`k`s`ks``s`k`s`kk``s``s`ksk`k``s`k`s`k`si``s`k`s`kk``s`k
     `sik`k`kk`k`k`kik`k`ki`k````s`ksk``s``s`kski``s``s`ksk``s``s`ksk``s``s`ksk`
     `s``s`kski````s``s``s`ksk`k``sii``s``s`ksk`k``sii``s`k`s``s``s`ki``s``si`k`
     k`k`k`ki`kk`k`ki``s`k`s``s`k``s`ks``s`k`s`ks`s`kk``si`kk``s``s`ksk`k``si`k`
     ki````s``s``s`ksk`k``sii``s``s`ksk`k``sii``s`k`s``s``s`ki``s``si`k`k`k`k`ki
     `kk`k`ki``s`k`s``s`k```s``s``s`ksk`k``sii``s``s`ksk`k``sii``s`k`s``s`ks``s`
     ki``s``si`k`k`k`k`ki`kk``s`k`s``s`ks``s`kk``s`k``s``s`ks``s`kk``s`ks``s`k`s
     ik`kk``si`kk``s``s`ksk`k``si`k`ki``si`kk``s``s`ksk`k``si`k`ki`````s``s``s`k
     sk`k``sii``s``s`ksk`k``sii``s`k`s`k`s``s``s`ki``s``si`k`k`k`k`ki`kk`k`ki``s
     `k`s``s`ks``s`k`s`k``s``s`ks``s`kk``s`ks``s`k`sik`kk``s``s`ksk`k``si`kk``s`
     `s`ks``s`k`s`ks`s`kk`k`k``si`k`ki``s``s`k```s``s``s`ksk`k``sii``s``s`ksk`k`
     `sii``s`k`s`k`s``s``s`ki``s``si`k`k`k`k`ki`kk`k`ki``s`k`s``s`ks``s`k`s`k``s
     ``s`ks``s`kk``s`ks``s`k`sik`kk``s``s`ksk`k``si`kk``s``s`ks``s`k`s`ks`s`kk`k
     `k``si`k`ki``s`ksk`k`````s``s``s`ksk`k``sii``s``s`ksk`k``sii``s`k`s`k`s``s`
     `s`ki``s``si`k`k`k`k`ki`kk`k`ki``s`k`s``s`ks``s`k`s`k``s``s`ks``s`kk``s`ks`
     `s`k`sik`kk``s``s`ksk`k``si`kk``s``s`ks``s`k`s`ks`s`kk`k`k``si`k`ki`s``s`ks
     k````s``s``s`ksk`k``sii``s``s`ksk`k``sii``s`k`s``s``s`ks``s`kk``s`ks``s`k`s
     ik`kk``s`k`s``s``s`ki``s``si`k`k`ki`kk`k`ki``s``s`ksk`k``s``s`ks``s`k`s`ks`
     `s``s`ks``s`k`s`ks``s`k`s`kk``s``s`ksk`k``s`k`s`k`si``s`k`s`kk``s`k`sik`k`k
     k`k`k`ki```s``s`ksk``s``s`kski``s``s`kski`````s``s``s`ksk`k``sii``s``s`ksk`
     k``sii``s`k`s`k`s``s``s`ki``s``si`k`k`k`k`ki`kk`k`ki``s`k`s``s`ks``s`k`s`k`
     `s``s`ks``s`kk``s`ks``s`k`sik`kk``s``s`ksk`k``si`kk``s``s`ks``s`k`s`ks`s`kk
     `k`k``si`k`ki`s``s`ksk````s``s``s`ksk`k``sii``s``s`ksk`k``sii``s`k`s``s``s`
     ks``s`kk``s`ks``s`k`sik`kk``s`k`s``s``s`ki``s``si`k`k`ki`kk`k`ki``s``s`ksk`
     k``s``s`ks``s`k`s`ks``s``s`ks``s`k`s`ks``s`k`s`kk``s``s`ksk`k``s`k`s`k`si``
     s`k`s`kk``s`k`sik`k`kk`k`k`ki```s``s`ksk``s``s`kski``s``s`kski```sii```sii`
     `s``s`kski

実行する

この MAIN を変換したものを実行します。結果が表示されるまでに3分くらいかかりますけどね。

f:id:todays_mitsui:20161030123900p:plain

はい。


まとめ

Lazy_K を使うプロジェクトで人が足りなくなったらいつでも呼んでください。


私からは以上です。

九九表のすべてのマスの和

takatoh 様 が同じ問題を Ruby と Scheme で解いてくださいました。許可を得て転記させていたいています
Ruby 版の簡素さはさすがという感じです。そして、Scheme 版の S式 は慣れないと読めないし分からないのが面白いですね。takatoh 様、本当にありがとうございます。


f:id:todays_mitsui:20161012233845p:plain


小ネタです。九九表の81マスに書かれている全ての数を足し合わせる計算をやってみましょう。
いろんな言語で書きます。

意味?特に無いですね。


Python2

import itertools
print(sum(map(lambda (x, y): x*y, itertools.product(range(1, 10), repeat=2))))
# => 2025

実行結果

途中で直積を求める必要があるんですが、itertools を使えば一撃です。さすが。


PHP

<?php
$sum = 0;

foreach (range(1, 9) as $i) {
    foreach (range(1, 9) as $j) {
        $sum += $i*$j;
    }
}

echo $sum;
// => 2025

実行結果

もっと関数型っぽくして行圧縮したかったんですけど、PHP なので潔く PHP らしく。

PHP で直積計算 も出来るようですが、直積だけで20行使ってるのにオエッとなりました。
3引数以上にも対応した一般的な書き方だからですかねぇ。


Haskell

import Control.Applicative
main = putStrLn . show . sum $ (*) <$> [1..9] <*> [1..9]
-- => 2025

実行結果

私が最も愛する言語 Haskell です。

Applicative Functor が便利ですね。
(,) <$> xs <*> ys で直積が取れるし (*) <$> xs <*> ys で直積の生成をすっ飛ばして九九表の全てのマスの生成ができます。


JavaScript (ES2015)

console.log([].concat(...[...Array(9).keys()].map(i=>i+1).map((i,_,ns)=>ns.map(j=>i*j))).reduce((i,j)=>i+j,0))
// => 2025

実行結果

たぶん初見では読めないので適度に分解すると、

// range(1, 9) の代わり
const ns = [...Array(9).keys()].map(i => i+1);

// flatten() の代わり
const flatten = arr => [].concat(...arr);

// sum() の代わり
const sum = ns => ns.reduce((i, j) => i+j, 0);

let result = sum(flatten(ns.map((i, _, ms) => ms.map(j => i*j))));
console.log(result);
// => 2025

こうやってみると JavaScript 貧弱ですねぇ。個人的には好きなんですけど。

Array.range()Array.flatten() も Set.product()Math.sum() も無いとかさすがにツラいです。
直積計算を Array.prototype.map() で無理やりやってるところがダサさポイントですね。


Lazy_K

`k`````s``s``s`ksk`k``sii``s``s`ksk`k``sii``s`k`s``s`ks``s`ki``s``si`k`k`k`k`ki`
kk``s`k`s``s`ks``s`kk``s`k``s``s`ks``s`kk``s`ks``s`k`sik`kk``si`kk``s``s`ksk`k``
si`k`ki```s`k````s``s``s`ksk`k``sii``s``s`ksk`k``sii``s`k`s``s`ks``s`k`s``s`ki``
s``si`k`k`k`k`ki`kkk``s``s`ks``s`k`s`ks``s``s`ks``s`kk``s`ksk`k``s`k`s``s`k``s``
s`ks``s`kk``s`ks``s`k`sik`kk``si`kkk`k`k``si`k`ki```sii```sii``s``s`kski```s``s`
`s`ksk`k``sii``s``s`ksk`k``sii``s`k`s``s`k``s``s`ks``s`kk``s`ks``s`k`sik`kk``s`k
```s`ks``s`k`s`ks`s`kk````s`ks``s`k`s`ks`s`kk````s`ksk```sii``s``s`kski````s`ksk
``s``s`kski``s``s`ksk``s``s`ksk``s``s`ksk``s``s`kski```s``s`ksk``s``s`kski``s``s
`kski``s```s``s``s`ksk`k``sii``s``s`ksk`k``sii``s`k`s``s`ks``s``s`ks``s`k`s`ki``
s`k`s`k``s``si`k`k`ki`kk``s`k`s``si`k``s``s`ks``s`k`s`ks``s``s`ks``s`k`s`ks``s`k
`s`kk``s``s`ksk`k``s`k`s`k`si``s`k`s`kk``s`k`sik`k`kk`k`k`kik``s``s`ks``s``s`ks`
`s`k`s`ki``s`k`s`k``s``si`k`k`ki`kk``s`k`s``s`k`s``si`k``s``s`ks``s`k`s`ks``s``s
`ks``s`k`s`ks``s`k`s`kk``s``s`ksk`k``s`k`s`k`si``s`k`s`kk``s`k`sik`k`kk`k`k`kikk
`k`k`kik``s``s`ks``s`k`s`ks``s``s`ks``s`kk``s`ksk`k``s`k`s``si`k``s``s`ks``s`k`s
`ks``s``s`ks``s`k`s`ks``s`k`s`kk``s``s`ksk`k``s`k`s`k`si``s`k`s`kk``s`k`sik`k`kk
`k`k`kik`k`ki`k````s`ksk``s``s`kski``s``s`ksk``s``s`ksk``s``s`ksk``s``s`kski``s`
k`s``s``s`ki``s``s`k`s`k``s``si`k`k`ki`kk``s`k`s``si`k``s``s`ks``s`k`s`ks``s``s`
ks``s`k`s`ks``s`k`s`kk``s``s`ksk`k``s`k`s`k`si``s`k`s`kk``s`k`sik`k`kk`k`k`kik`k
```s``s`kski``s``s`ksk``s``s`kski`k`ki``s``s`ksk`k``s```s``s``s`ksk`k``sii``s``s
`ksk`k``sii``s`k`s``s`ks``s``s`ks``s`k`s`ki``s`k`s`k``s``si`k`k`ki`kk``s`k`s``si
`k``s``s`ks``s`k`s`ks``s``s`ks``s`k`s`ks``s`k`s`kk``s``s`ksk`k``s`k`s`k`si``s`k`
s`kk``s`k`sik`k`kk`k`k`kik``s``s`ks``s``s`ks``s`k`s`ki``s`k`s`k``s``si`k`k`ki`kk
``s`k`s``s`k`s``si`k``s``s`ks``s`k`s`ks``s``s`ks``s`k`s`ks``s`k`s`kk``s``s`ksk`k
``s`k`s`k`si``s`k`s`kk``s`k`sik`k`kk`k`k`kikk`k`ki`k`k`ki``s`k`s`k`s`k`s``s`ksk`
`s``s`ks``s`k`s`ks``s``s`ks``s`kk``s`ksk`k``s`k`s``si`k``s``s`ks``s`k`s`ks``s``s
`ks``s`k`s`ks``s`k`s`kk``s``s`ksk`k``s`k`s`k`si``s`k`s`kk``s`k`sik`k`kk`k`k`kik`
k`ki`k````s`ksk``s``s`kski``s``s`ksk``s``s`ksk``s``s`ksk``s``s`kski````s``s``s`k
sk`k``sii``s``s`ksk`k``sii``s`k`s``s``s`ki``s``si`k`k`k`k`ki`kk`k`ki``s`k`s``s`k
``s`ks``s`k`s`ks`s`kk``si`kk``s``s`ksk`k``si`k`ki````s``s``s`ksk`k``sii``s``s`ks
k`k``sii``s`k`s``s``s`ki``s``si`k`k`k`k`ki`kk`k`ki``s`k`s``s`k```s``s``s`ksk`k``
sii``s``s`ksk`k``sii``s`k`s``s`ks``s`ki``s``si`k`k`k`k`ki`kk``s`k`s``s`ks``s`kk`
`s`k``s``s`ks``s`kk``s`ks``s`k`sik`kk``si`kk``s``s`ksk`k``si`k`ki``si`kk``s``s`k
sk`k``si`k`ki`````s``s``s`ksk`k``sii``s``s`ksk`k``sii``s`k`s`k`s``s``s`ki``s``si
`k`k`k`k`ki`kk`k`ki``s`k`s``s`ks``s`k`s`k``s``s`ks``s`kk``s`ks``s`k`sik`kk``s``s
`ksk`k``si`kk``s``s`ks``s`k`s`ks`s`kk`k`k``si`k`ki``s``s`k```s``s``s`ksk`k``sii`
`s``s`ksk`k``sii``s`k`s`k`s``s``s`ki``s``si`k`k`k`k`ki`kk`k`ki``s`k`s``s`ks``s`k
`s`k``s``s`ks``s`kk``s`ks``s`k`sik`kk``s``s`ksk`k``si`kk``s``s`ks``s`k`s`ks`s`kk
`k`k``si`k`ki``s`ksk`k`````s``s``s`ksk`k``sii``s``s`ksk`k``sii``s`k`s`k`s``s``s`
ki``s``si`k`k`k`k`ki`kk`k`ki``s`k`s``s`ks``s`k`s`k``s``s`ks``s`kk``s`ks``s`k`sik
`kk``s``s`ksk`k``si`kk``s``s`ks``s`k`s`ks`s`kk`k`k``si`k`ki`s``s`ksk````s``s``s`
ksk`k``sii``s``s`ksk`k``sii``s`k`s``s``s`ks``s`kk``s`ks``s`k`sik`kk``s`k`s``s``s
`ki``s``si`k`k`ki`kk`k`ki``s``s`ksk`k``s``s`ks``s`k`s`ks``s``s`ks``s`k`s`ks``s`k
`s`kk``s``s`ksk`k``s`k`s`k`si``s`k`s`kk``s`k`sik`k`kk`k`k`ki```s``s`ksk``s``s`ks
ki``s``s`kski`````s``s``s`ksk`k``sii``s``s`ksk`k``sii``s`k`s`k`s``s``s`ki``s``si
`k`k`k`k`ki`kk`k`ki``s`k`s``s`ks``s`k`s`k``s``s`ks``s`kk``s`ks``s`k`sik`kk``s``s
`ksk`k``si`kk``s``s`ks``s`k`s`ks`s`kk`k`k``si`k`ki`s``s`ksk````s``s``s`ksk`k``si
i``s``s`ksk`k``sii``s`k`s``s``s`ks``s`kk``s`ks``s`k`sik`kk``s`k`s``s``s`ki``s``s
i`k`k`ki`kk`k`ki``s``s`ksk`k``s``s`ks``s`k`s`ks``s``s`ks``s`k`s`ks``s`k`s`kk``s`
`s`ksk`k``s`k`s`k`si``s`k`s`kk``s`k`sik`k`kk`k`k`ki```s``s`ksk``s``s`kski``s``s`
kski```sii```sii``s``s`kski

書きました!
Lazy K Playground で走らせてみたら結果を得るまでに150秒以上かかりました...。


Ruby

九九表のすべてのマスの和 | blog.PanicBlanket.com より転記。

# encoding: utf-8


def sum_of_kuku
  a = (1..9).to_a
  a.product(a).map{|x,y| x * y}.inject(:+)
end


puts sum_of_kuku

なるほど .inject(:+) のようなメソッド呼び出しで sum() が実現できるんですね。
.product() が標準で提供されているのも流石 Ruby といった感じ。


Scheme

九九表のすべてのマスの和 | blog.PanicBlanket.com より転記。

(use srfi-1)

(define direct-product
  (lambda (lis1 lis2)
    (append-map
      (lambda (x) (map (lambda (y) (list x y)) lis2))
      lis1)))


(define sum-of-kuku
  (lambda ()
    (let ((l1 '(1 2 3 4 5 6 7 8 9))
          (l2 '(1 2 3 4 5 6 7 8 9)))
      (apply + (map (lambda (x) (apply * x)) (direct-product l1 l2))))))


(print (sum-of-kuku))

読めない...。
(apply + (map (lambda (x) (apply * x)) (direct-product l1 l2))) のあたりが本題なんだろうなという感じはしますが、式が評価される様を頭でイメージできなければ、コードの解読も厳しい感じがしますね。


私からは以上です。