星田さんの記事に関するコメント。
pythonの方は超絶基本的な内容だけど13歳に向けての本だし、、一応cametanさん式判別のためにforのところを見たらリスト内包表記は無いけど「まず変数に代入してlenで長さ調べて〜」とかはやってなかったです。
そりゃ良かった(笑)。
っつーかそれも原本は洋書でしょ?
洋書はだから、そこそこ評判が良い、って結果になってないと翻訳されないんですよ。
そしてAPRESSって出版社は、オライリーの陰に隠れて見えづらいけど、実績がある。
そうなると、クオリティ的には、結局「凡百の日本のPython本の駄作」が太刀打ち出来るわけがないんです。
C言語の方は、、未だに新刊で普通のCの本が出るんですねえ、、、string型が無いとか(ビビった)、配列使ったら強制的にポインタ必須になるとか、やっぱり毎回コンパイルがいるとか、、大変だなあと。
そう、C言語には文字列はないんです。だから気の利いた本だと、そもそも文字列とは表現しない。文字配列と呼んだりする。読んで字の如く「文字の配列」です。
そしてC言語には文字もない(笑)。これも単純には数値です。ただ、「1バイトとしてひと塊になって扱えるブツを」char型、って呼んでるだけ。そして「バイトと言う定義は存在しない」し、単純にある数値の塊を纏めて扱ってる、ってのがその正体(※1)。
だからもう、マジで低レベルで、C言語は「移植性があるアセンブリ言語」と言われてる所以です(※2)。
なお、Spacemacsではdotspacemacs-configuration-layersにc-c++と記述すればC/C++用ツールが一式ダウンロードされて設定されます。
spacemacsに於けるC言語用ツール設定例(Linux)
まぁ、実際は、Windowsで試すならVisual C++をインストールした方がラクだとは思いますけどね。
長い間、MicrosoftのCコンパイラはISO Cの標準に従ってなかったんですが、最近は最新のC17と言う標準仕様に準拠するようになりました。よって「標準」の問題は解決された(※3)。
あるいは、フリーなCコンパイラの定番、mingwをダウンロード/インストールするのも手、です。
これはLinuxの標準的Cコンパイラ、gcc(GNU Compiler Collection)のWindows版です。
C-x C-fでここではhello.cと言うファイルを作る。Emacsはcと言う拡張子を見るとCモードを起動する。
hello.cと言うバッファを開いたら、space、'、と打つ。そうするとバッファの下段が開き、端末(WindowsだとDOS窓、Linuxだとshell)が開く。原理的にはCコンパイラはスクリプトに近く、端末でコマンドとして実行するプログラムである。
当然、C言語だろうと自動補完は働く。
プログラムを書き上げたらC-x C-sでセーブする。次は端末にカーソルを持っていこう(マウスで良いが、C-x oで今いる場所からカーソルを移動可能)。
gccでの基本的なコンパイルはgcc ソースファイル名.cで行われる。この場合、コンパイル済みの実行用ファイルは必ずa.out(Windowsだとa.exe?)になる。実行ファイルに特定の名前をつけたい場合はgcc ソースファイル名.c -o 実行ファイル名.exeとなる。-oを「オプション」と呼び、このオプションには色々とある。詳しくはマニュアルを参照の事。
コンパイルが終了すれば(そもそも何も表示されないが・笑)端末から./a.out(Windowsだとa.exe?)を呼び出すとコンパイル済みのプログラムが実行される。
と言うのがSpacemacsでのC言語開発です。
一般に、インタプリタでの開発と違って、
- 端末上でコンパイル・実行した結果を見ながら開発していく
と言う特徴があります。
逆に言うと、何故にC言語脳がPythonコードに不必要にprintを書きまくるのか、と言うと、このテの開発法の手癖なんです(※4)。
他の3つは分かったけどreduceは確かに分からない(^_^;) 定義しっかり見てみよう。
うん、これは皆悩むんだわ(笑)。だからScheme標準に取り上げられてないのかな、とか思う。
でも、これをマスターすると非常に強力なんです。強調しても強調し過ぎる事はない。
Pythonでもfunctools.reduce(あるいはitertools.accumulate)がある以上、覚える価値があるし、これを知ってるか知らないか、でコードの圧縮率が桁違いになる。
圧縮すりゃエエ、ってもんじゃない、って意見もあるだろうけど、コードの「見た目」が段違いによくなるんですよ。ダラダラと書かれたコードを読むよか明らかにラクになる。
例えばだよ。
今月のFANZA AV女優ランキング第一位は河北彩花なる人らしいんですが。
「おう、結構可愛いじゃん」とか言う個人的な趣味は置いておいて(謎
例によってまずはこれらをrequireして。
(require 2htdp/image 2htdp/universe)
そしてRacketで画像の読み込みをする。
んで例えば、続いてこう言うカンジで定義していく。
;;; 河北彩花を一つ置く関数
(define (place-saika s scene)
(place-image *saika-image*
(car s)
(cdr s)
scene))
;;; 再帰関数定義(河北彩花を複数置く関数)
(define (place-saikas saikas scene)
(if (null? saikas)
scene
(place-saikas (cdr saikas)
(place-saika (car saikas) scene))))
;;; 河北彩花の写真3つを置く場所を定義
(define *saikas* '((600 . 300) (30 . 370) (400 . 230)))
;;; big-bang から呼び出す描画関数(一種ダミー)
(define (place-world w)
(place-saikas *saikas*
(empty-scene 800 500 "Medium Pink")))
;;; イベントループ
(big-bang #f
(to-draw place-world)
(name "河北彩花"))
これはゲームじゃない。単に河北彩花の写真を3つ置いたウィンドウを表示する、だけだ(だからWorld構造体もクソもない -> #f を与えてるだけ)。
結果、こういうウィンドウが表示される。
このコードのキモは当然次の2つの関数になる。
;;; 河北彩花を一つ置く関数
(define (place-saika s scene)
(place-image *saika-image*
(car s)
(cdr s)
scene))
;;; 再帰関数定義(河北彩花を複数置く関数)
(define (place-saikas saikas scene)
(if (null? saikas)
scene
(place-saikas (cdr saikas)
(place-saika (car saikas) scene))))
当然、
- 河北彩花の写真を1枚だけ置く関数を定義する。
- 1.の関数を再帰的に適用する事によって複数の河北彩花の写真を置く。
なわけだ。
で、だ。ポイントは2番なんだけど。
「再帰的に適用する」のがメンド臭い場合、当然reduce(Racket組み込みではfoldl)の出番となる。
これがreduceを使ったplace-saikas関数の改良版だ。
;;; reduceを使った関数定義(河北彩花を複数置く関数)
(define (place-saikas saikas scene)
(foldl place-saika scene saikas))
5行あったplace-saikas関数がたった2行に圧縮される。半分以上の圧縮率だ。そして当然ながら実行結果は変わらない。これがreduceの威力。
ポイントとしてはまずnull?検査が無くなってる。その代わり「初期値」(この場合、レイヤー最下部のscene)と言う概念が出てきてるわけだ。
結果、foldlが何を生んでるのか、と言うと、このケースの場合、
(place-saika (third saikas) (place-saika (second saikas) (place-saika (first saikas) scene)))
と言う初期値(scene)、から始まってのplace-saika関数の連続適用だ。
これは単一用のplace-saika関数の定義から言うと「正しく」適用されてるし、結果「画像レイヤーを重ね合わせていく」効果と符合する。
これが畳み込み関数と言われる所以。
んで、書式的にはfoldlの第一引数である関数(この場合place-saika関数)の引数順序とfoldlの第二引数、第三引数の順序が逆になってる、って辺りが落とし穴かな(これは定義を見れば明らかでしょう)。
それさえ気をつければ、まずハマる事はないです(ハマったらラムダ式を被せて引数順序を交換してみればいいだけ、だし・笑)。
そして、reduce(foldl)は、「コンピュータサイエンスの宿題の為の宿題」ではなく、上で見た通り、明らかに実用性と有効性がある。っつーかメチャクチャ役立つ超強力高階関数です。画像処理だろうが、リスト処理だろうが、関係なく畳み込んでしまう恐ろしい関数だ、と言うのが上の例になっています。
これも!
うん、合成関数自体は数学上の概念だけど(高校でやってる?)、それ自体とはまったく関係なく、単に便利です。
関数composeは、2つ(以上の)関数を「順序良く」貼り合わせて、要するに貼り合わせたモノのクロージャを返します。従って、2つ(以上の)関数を「順番に適用する」ような関数が返ってくる、って事ですね。
これもコードを圧縮する効果があるんで、バンバン使って構いません。
いや、むしろ使え(笑)。
あとようやくマクロって結局放置してるのでsyntax-rulesのところを再読。前よりはなんか分かりそうな予感がする、、
おお、いよいよLispの黒魔術に(笑)。
要するにあの形式ならifとかスコープ?無視の書き換えとか通常では許されない動作を許す関数みたいなのを書けるって事かな?
う〜ん、いや、それは結構Schemeは厳格、で(笑)。
そっちの「ホンマ黒魔術」は基本、ANSI Common Lispのモノかな・・・・・・?
いや、実はポール・グレアムのaifの実装はSchemeだとどうするんだ、って話があって。
結論から言うと、define-syntax(+ syntax-rules)だとそこまで強力じゃないのね。Schemeは「正しい」言語だから、どこに至っても原則、レキシカルスコープの呪縛からは逃れられません。
ANSI Common Lispのマクロ並に強力なのは、R6RSから導入されたsyntax-caseなんですが、ANSI Common Lispより安全な代わりに、書くのが相当メンド臭いです(笑)。また、これも特に「指南書」が今のトコないんですよねぇ。
だから僕も「よう分からん」です(笑)。例によってRacketのドキュメントだとイミフだしね。
※1: これは元々C言語が、どのミニコンに持っていっても使える前提として設計された為。
今ではアスキーコードが最低でも標準となっているが(こういうのをデファクトスタンダード、と呼ぶ)、1963年に制定されたこれは、C言語登場時、どのミニコンでも標準だった、と言うわけではない。
「文字」をどう定義するのか、と言うのはマシンによって違っていて、必ずしもアスキーのように7bit枠に収まってるわけではなく、10bitだったり20bitだったり、24bitだったり、と今の状況で考えるとメチャクチャだったのだ(笑)。
Cのchar型、と言うのはその「8bitだったり」「10bitだったり」「20bit」だったりする数値の「塊」を纏めて1バイトとして扱うように設計されていて、その辺「どの大きさの塊をバイト(ないしはワード)とするか」と言うのは各OS毎の判断に任せる、と責任をぶん投げてるのが基本である。
なお、結果として、C言語は他の高級言語で言う「データ型」ないしは「オブジェクト」と言うモノを元々持ってはいない。あくまで暫定的に「数値をどう解釈するか」と言う機能しかなく、結果、文字と文字の足し算、とか文字と文字の引き算、と言う他の言語じゃ考えられないような事が出来てしまうのだ(そしてそれはPythonのような「オブジェクト指向による」ポリモーフィズムとは全く意味が違っていて、単なる数値の足し算や引き算となる)。
※2: 「ポータブルなアセンブリ」と言うのがあだ名である。
※3: 実はニュアンスとしては、長い間、Microsoftの処理系は「C言語コンパイラはC++コンパイラのおまけである」程度の扱いで、Microsoftとしての推しはあくまでC++だったのだ。
従って、MicrosoftはISO C++には徹底的に準拠するように実装してた歴史があり、Cコンパイラはほったらかしだったのだが、この度(2018年)、Cの世界標準にも準拠する事にあいなったのである。
なお、何故にオープンソース界でMicrosoftの評判が悪かったのか、と言うと、Internet Explorerの例を見ても分かる通り、「標準に従わず力ずくでMicrosoftの独自仕様をデファクトスタンダード化しよう」とやってきた前科がある為、である。
ただし、最近のMicrosoftはこういう剛田武的な行いを改めて来てはいる(と言うか、そうしないとGoogleに勝てない、と分かってきたようだ)。
また探せばC言語インタプリタ、と言うモノもこの世には存在する。
が、ユーザーの要求及び実用性で、C言語コンパイラの方がマジョリティであり、C言語インタプリタは細々と教育目的用に作られたりしてるだけ、である。