同じ変数名でなくてもいい部分を同じ変数名にして初心者を混乱させてしまう現象

最近はProgateでRubyを中心にいろいろ未知の言語に触れている。

prog-8.com

Progate、控えめに言ってグレイトすぎるサービス。勉強法についてこれほど意識的なプログラミング学習サービスを他に知らない。「回転」や「定着」や「インセンティブ(ごほうび)」みたいなものが学習者にもたらす効果について、これ以上なくストイックに追求していると思う。リスペクト。

さてしかし、そのProgateのRuby学習コースIVの中で、ありがちな不備というか、これはまったくProgateに限ったことではないのだけど、すでにプログラミングをよく理解している人が初心者に教えたり入門書を書いたりするときにやりがちな悪例(というか推奨できない教え方)を目にしたので、記しておきたい。

具体的には、上記コースの「インスタンスメソッド」の項をやっているときに、こんなサンプルコードに出会った。(部分的に編集して抜粋)

class Menu
  attr_accessor :name
  attr_accessor :price
  
  def initialize(name:, price:)
    self.name = name
    self.price = price
  end
  
  def info
    return "#{self.name} #{self.price}"
  end
end

menu1 = Menu.new(name:"すし", price:1000)

puts menu1.info

実行すると、こんな。

すし 1000円

なんの変哲もない、シンプルなサンプルコードのように見えるけど、じつはここには初心者を混乱に陥れる問題が潜んでいる。

それはどこか? たとえば、ここ。

  def initialize(name:, price:)
    self.name = name
    self.price = price
  end

「name」と「price」という変数名が、本来必要な数に比べて多すぎる。

その言い方でわかりづらければ、以下の修正例を見てほしい。

  def initialize(name_foo:, price_bar:)
    self.name = name_foo
    self.price = price_bar
  end

先ほどはnameとpriceが3個ずつ出てきたが、ここでは1個ずつあるだけで、あとはname_fooとprice_barに置き換えられている。

さて、ここでは一体、何を直したのだろうか? それは、

  • 同じ変数名じゃなくてもいい部分にも同じ変数名を使っている

という状況を、

  • 同じ変数名じゃなきゃいけないところだけを同じ変数名にしている

という状況に変えた、ということになる。

最初のサンプルコードでは、「本来なら別の変数名でも構わないところ」が、なぜか(たぶん何となく)「同じ変数名」になっている。そして、初心者がそれを見てどう思うのかというと、それらの変数名が「同じでなければいけない」と思ってしまう。

だから教える人は、自分たち経験者にとっての「同じ変数名でなくてもいい場所」が、初心者にとっては「同じ変数名であってはいけない場所」なのだということを理解しながら教えてあげなければいけない。

もちろん、ある程度習熟してくれば、これらの箇所で同じ変数名を使いたくなるのは自然なことだと思う。

だけど、「同じ変数名にしてもいい(場合によってはその方が便利)」ということと、「同じ変数名にしなければならない」ということはやはりまったく別のことだ。

そして初心者にとっては、「なぜ同じ変数名にしたのか」を説明されなければ、それは「同じ変数名にしなければならない」という意味になる。

どうもこのたぐいのズレがプログラミングの入門書では散見される。そしてそのつど、「ああ、わかってないな・・」と思ってしまう。*1

変数名というのは、その中身がどんな値であるのか、外側から(中身を見なくても)大体判断できるように付ける目印というか、外装みたいなものだろう。

プログラミング経験者は、コード全体の文脈を踏まえて変数を見ているから、その中身をすぐに想像できるだろうけど、初心者はそうではない。初心者は同じ変数名を見たら、中身も同じなのだろうと思ってしまう。あるいは、「中身は違うかもしれない・・けど、確証はない・・」という状態。言い換えると、変数の中身を頭の中でトレースできていない。

中身も役割も異なるのに、同じ変数名が付いているというのは、人間で言ったら「別人なのに全身同じ服」みたいなもので、だからプログラミング初心者にとっての「値が異なるのに同じ変数名」という状況は、欧米人にとっての「背格好がほぼ同じでまったく同じ服を着ている日本人」みたいなもので、見た目から中身が異なることを判断するのは難しい。というか単純にまぎらわしい。

変数名は中身を想像させるために付けているのだから、経験者ほどにはその中身を想像できない初心者に対して、そういうまぎらわしいことをしてはいけないと思う。外から見ただけで、「ああ、あそこで使った変数をここでも使い回せるのか」と直感的にわかった方が、より効率的に、本質的な構文を学びやすくなるのではないだろうか。

話を戻して、上記の修正はメソッドの呼び出し部分にも影響を及ぼすから、一応全体に修正を行き渡らせたサンプルも示しておく。

class Menu
  attr_accessor :name
  attr_accessor :price
  
  def initialize(name_foo:, price_bar:)
    self.name = name_foo
    self.price = price_bar
  end
  
  def info
    return "#{self.name} #{self.price}"
  end
end

menu1 = Menu.new(name_foo:"すし", price_bar:1000)

puts menu1.info

https://i.gyazo.com/b61dff70f93446898a9fa85426e0e0e8.gif

しかしこの、「同じ変数名でなくてもいい部分」をつい普段のクセで「同じ変数名」にしてそのまま入門書などに書いてしまう現象、なにか名前をつけたい。そしてこうした問題がくり返されないための目印みたいなものにしたい。

*1:とはいえ、本の場合はこういうズレは編集者さんが見つけるべきだと思うけど。執筆者はプログラミングのプロではあっても、入門書のプロであるとは限らないわけだから。