2019-10-03

また初心者にプログラミングを教える機会があった

プログラミングでわからないところがあるので教えてほしいと以下のようなことを聞かれた。

こういうJavaScriptの関数がある。

// valuesは配列
// elementはvaluesの要素型の値
// 配列valuesに値elementと等しい要素があるならばそのインデックスを返す。
// それ以外の場合、-1を返す
function find_index( values, element )
{
    for ( let i = 0 ; i !== values.length ; ++i )
    {
        if ( values[i] === element )
            return i ;
    }
    return -1 ;
}

質問は、「なぜreturn -1にelseはいらないのか」というものであった。

似たような問題に、昔遭遇した気がするが、別人だ。

まずここにelseを書くべき文法はJavaScriptに存在しない。if文で何らかの条件を切り分ける必要もない。なぜならば、return -1が評価されるとき、すでにforループを抜けているわけで、その場合要素が見つからなかったということだ。逆に、要素が見つかったのであれば、すでに上のreturn iが評価されているので、すでに処理は関数の呼び出し元に戻っており、return -1は評価されることがない。

ただ、このような机上の説明を繰り返しても理解ができない様子であったので、さらにデバッガーでステップ実行してみせるなどして説明した。

この問題は、逐次実行という概念と、逐次実行がfor文やif文やreturn文によって変わるということ、そしてプログラミングにおける関数の理解が必要だ。しかし、筆者はこのような概念の理解に苦労した覚えはないし、周りの職業プログラマーに聞いても、やはり苦労した覚えはないという。

しかし不思議だ。質問者は数学の素養があり、数学における関数なら理解しているはずだ。聞けば再帰も理解しているという。それならと以下のように再帰で書いてみた。


function find_index( values, element )
{
    function solve( i )
    {
        if ( i === values.length )
            return -1 ;

        if ( values[i] === element )
            return i ;

        return solve( i + 1 ) ;
    }
    return solve(0) ;
}

これを何の説明もせずに見せたところ、「これはとても良くわかる。なんでみんなこう書いてくれないのか」とのことであった。質問者はJavaScriptの初歩の初歩しか学んでおらず、このようなコードは見たことがないはずだ。しかしわかりやすいと言う。再帰は正しく理解できていることが確認できた。

質問者にはHaskellのような純粋関数型の言語のほうが向いているのかもしれない。

12 comments:

Anonymous said...

純粋な関数に対して確固たるイメージを持っている感じがする
サブルーチンと言えば納得できるのだろうか

Anonymous said...

やはりforも数学の総和と同じく宣言的に見えていたのだろうか?

Anonymous said...

returnをcontinueだと思ってたのか……?

Anonymous said...

for ... else を1つのまとまりとして値を返す処理単位に見えていた?
我々は「forは繰り返し処理を行うが内部でreturnしなくとも別に何ともない」という共通認識を持っているが、
「forは値を返す」という認識であるなら「forの内部でreturnしない場合にはelseでreturnしなければ」という考えになってもおかしくない。

Anonymous said...

 皆さんのおっしゃることも良く分かるのですが・・・。
 最終的にはノイマン型プログラム・ストアード方式のコンピュータで実行する限り、制御は無条件or条件ジャンプによってフェッチ・アドレスを強制的に変更することにより行われ、例えその高級言語が目も眩むような素晴らしい制御構造を持つとしても全ては見せかけの虚構であって、結局は先の無条件or条件ジャンプに書き換えられる訳です。したがって実態だけを見つめて、関数なんて機構は存在せずサブルーチンがあるだけと極論することもできますし、逆に見せかけの奥底に潜む実態を無視すれば、関数とは「多様体上に植え付けら得たファイバー束の切断」とか妄想猛々しく(アブストラクト・ナンセンスを)語ることも可能です。(「高級言語=人間に分かりやすい言葉や概念を使ってプログラミングができること」が原意ですよね〜。己の脳みその発達具合をひけらかすための小道具ではないと思いますが、「Haskell?圏論の理解が必要?? そんな小難しいことを理解しないとプログラムできないのか!?」とか思いますね。)
 で、件の質問者ですが、数学の素養があるのは結構なのですが、コンピュータがfor文とかreturn文をどのように実行しているのかそのカラクリには興味を持たないとか、普通の人間には理解しづらい再帰を「これはとても良くわかる。なんでみんなこう書いてくれないのか」と言ってる時点で、エンジニアとしての素質に欠けるというか、知能が少々イビツな方向に発達しているというか。
 私なら、バッカス・ナウア記法で書かれた文法定義でも見せて、如何に愚かな質問をしているのか悟らせますが。何せ、数学の素養があり再帰がとても良くわかるんでしょ?

said...

へー、こりゃ面白いね
さっそくマクロにして文法追加しとこう
上手くいって抜けると、失敗で次への
2つが要るね。cond-map かな
こりゃ便利だわ

Anonymous said...

>さっそくマクロにして文法追加しとこう

マクロで文法追加ね〜。下手なことは止めといた方が良いと思うけど・・・。

abo_junghichi said...

近年の大抵のJavaScriptの処理系は、共通式削除やループ不変量コード移動や命令再スケジューリング等、実行時コンパイラがコードをグチャグチャに改変した上に、キャッシュが命令用とデータ用に分かれたハーバード型アーキテクチャ寸前CPUが、store to load forwarding(メモリアクセス順序をグチャグチャにする)や投機実行などを駆使し、「命令順序? 何それ美味しいの?」と言わんばかりのアウト・オブ・オーダーで、先のグチャグチャ命令列を実行しています。
今日ではノイマン型プログラム・ストアード方式もまた、見せかけの奥底に潜む実態を無視したアブストラクト・ナンセンスの一種なのでしょう。

あるいは、変数への値の代入の禁じられている関数型言語の方が、そうではない命令型言語よりも、レジスタ・リネーミングを行う近年のCPUアーキテクチャに沿っているとも言えます。
レジスタなどないスタックマシンに至っては、「メモリ」リネーミングを行っているようです。
(PDF) BOOST: Berkeley's Out-of-Order Stack Thingy
https://www.researchgate.net/publication/228556746_BOOST_Berkeley's_Out-of-Order_Stack_Thingy
Tomasuloのアルゴリズムは意外と、数学の素養しかない人にはノイマン型ストアード方式より理解し易いかも知れません。

Anonymous said...

>キャッシュが命令用とデータ用に分かれたハーバード型アーキテクチャ寸前CPUが 〜「命令順序? 何それ美味しいの?」と言わんばかりのアウト・オブ・オーダーで

 うーんと、ネット上ならいざ知らず、この調子であまり詳しくないことの断片をつなぎ合わせたようなことは、リアルな世界では仰らない方がよろしいかと。
 スーパー・スカラなどのCPUは、命令の発行や実行がアウト・オブ・オーダーなだけで、命令の完了はイン・オーダです。したがって、プログラマから見た命令の実行順序は、機械語コードの並び順を厳密に保つことが担保されています。決して見せかけでも実態を無視してもいません。アウト・オブ・オーダーはCPU内の複数のパイプラインを有効に使うための技法であり、レジスタ・リネーミングはWAR(write after read)やWAW(write after read)などのハザードを回避して、それらのパイプラインがハザードにより徒にストールするのを防ぐための一つの方法です。関数型言語などとは直接の関係がある訳でもなく、プログラマからはコントロール不能なハードウェアにより制御されています。これらは何れもCPUの1クロックあたりの命令実行数を増加させ処理能力を上げることが目的であり、プログラム上の抽象的な見せかけを作ることを目的とはしていません。

Anonymous said...

>「forは値を返す」という認識であるなら「forの内部でreturnしない場合にはelseでreturnしなければ」という考えになってもおかしくない。

python の for には else ありますし
python なら向いてるかも知れません

Anonymous said...

長文オタクおって草
純粋関数型には純粋関数型の利点があるからそれなりに使われてるんだけどね

Anonymous said...

>長文オタクおって草
>純粋関数型には純粋関数型の利点があるからそれなりに使われてるんだけどね

純粋関数型の利点がどうのこうのって、別に初心者指導に対する主題じゃないんだけどね。
この程度の文章を「長文」って、普段どの程度のプログラムをしてるのかが知れて草。