やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

「字句構造」を読む

 予約語は知っておきたい。

成果物

情報源

字句構造

  • 識別子
  • コメント
  • 埋め込みドキュメント
  • 予約語

Rubyの現在の実装はASCIIキャラクタセットを用いています。アルファベットの大文字と小文字は区別されます。識別子と一部のリテラルの途中を除いては任意の場所に空白文字やコメントを置くことができます。空白文字とはスペース、タブ、垂直タブ、バックスペース、キャリッジリターン、ラインフィード、改ページです。改行は行が明らかに次の行に継続する時だけ、空白文字として、それ以外では文の区切りとして解釈されます。

 ASCIIなのか。日本語のコメントとかはどうなっているの? というか、たしかUCS正規化方式でなくCSI方式だったよね? 内部文字コードはバイナリじゃなかったの?

改行と認識されるのは、キャリッジリターン+ラインフィードかラインフィードです。

識別子

例:

foobar
ruby_is_simple

Rubyの識別子は英文字またはアンダースコア('')から始まり、英文字、アンダースコア('')または数字からなります。識別子の長さに制限はありません。

 つまり正規表現で言うと以下ってことかな? ほかのプログラミング言語と同じかな?

[_a-zA-Z][_a-zA-Z0-9]+

 でもさ、メソッドの末尾に!?が付与できるよね? あれは名前だって書いてあったんだけど。メソッドの末尾だけは例外ってこと? 一言も書いてないし。

コメント

例:

# this is a comment line

スクリプト言語の習慣にならい、文字列中や文字リテラル `?#' 以外の #から行末までをコメントと見なします。

埋め込みドキュメント

例:

=begin
the everything between a line beginning with `=begin' and
that with `=end' will be skipped by the interpreter.
=end

Rubyソースコードにドキュメントを埋め込む事ができます。文が始まる部分の行頭の=beginから、=endで始まる行までが埋め込みドキュメントです。Ruby インタプリタとしては内容に縛りはかけませんが、通常は RD 形式でドキュメントを埋め込むことを期待しています。

 RD形式というのがわからないが、rdocでHTMLに置換できる書式のことをそういうのだろう。これまでの公式ドキュメントにそんなことは書いていなかった気がするが。こんなところで初登場? rdocコマンド説明のときに出して欲しかった。

 あと、rdocはちゃんと#コメントも同様に解析する。むしろスタイルガイドでは#コメントを推奨している。

予約語

予約語は以下のものです。

BEGIN    class    ensure   nil      self     when
END      def      false    not      super    while
alias    defined? for      or       then     yield
and      do       if       redo     true     __LINE__
begin    else     in       rescue   undef    __FILE__
break    elsif    module   retry    unless   __ENCODING__
case     end      next     return   until

 これが欲しかった。知らないのもチラホラある。undef,super,alias,defined?。やはりasyncawaitはない。きっとそのうち標準実装されると信じているが。

予約語はクラス名、変数名などに用いることはできません。ただし接頭辞$, @、@@が先頭についたものは予約語とは見なされません。また、def のあとやメソッド呼び出しのピリオドのあとなどメソッド名であるとはっきり分かる場所ではメソッド名として用いることができます。

 グローバル変数インスタンス変数、クラス変数は接頭辞があるから名前重複を回避できる。メソッドも定義や呼出はほぼ回避できる。でも上記の言い方だと、メソッド名であるとはっきりわからない場所があって、そこでは名前重複してしまうと読み取れる。まったく思いつかないので実際に重複するコード例が欲しかった。

また、番号指定パラメータは変数名、def で定義するメソッド名に用いることはできません。

_1 _2 _3 _4 _5 _6 _7 _8 _9

番号指定パラメータ

 番号指定パラメータってなんだよ。まーた説明がないのでググった。

 Ruby2.7で導入されたらしい。ブロックの仮引数を連番名で受け取れる。

['A','B','C'].each {|v| p v}
['A','B','C'].each {p _1}
['A','B','C'].each_with_index {|v,i| p "#{i}:#{v}"}
['A','B','C'].each_with_index {p "#{_2}:#{_1}"}
  • 割り当てられなければnil
  • 使用できる連番は19の8個
  • _10のようにするとNameError
  • _1のようなローカル変数があればwarning
  • _1のようなローカル変数がブロック外の上で宣言されていたらローカル変数を参照してしまう
  • _1のようなメソッドがあると番号指定パラメータが参照される
  • ネストしたブロック内では1階層でしか使えない
  • 従来の仮引数と混在させるなら仮引数名を番号指定パラメータにせねばならない

使いどころ

 以下の条件を満たしているときだけに限って使うべきだと思う。

  • 仮引数がひとつである
  • 仮引数の内容が何であるかメソッド名からはっきりとわかる

 ちゃんと考えないと可読性がさがるから。

['A','B','C'].each {p _1}
['A','B','C'].each_with_index {p "#{_2}:#{_1}"}

 1つ目はまだいい。eachとうメソッド名からして要素がひとつずつ取得されることが想像できる。だから仮引数の内容は要素だと想像できる。

 2つ目は混乱する。_1は要素なのか、それともインデックスなのか。位置引数は名前がないためわからない。メソッド名からどの位置に何の値がくるかわかるものなんてそうそうないと思う。つまり、仮引数が2つ以上あるときは番号指定パラメータを使わないほうがいい。

 仮引数が1つのときだけ番号指定パラメータを使うべき。だとしたら_で仮引数を参照できるようにして欲しかった。でも以下のように_にしてしまうとエラーになった。

['A','B','C'].each {p _} # undefined local variable or method `_' for main:Object (NameError)

 以下のようにすれば_を仮引数として使えるが、それなら意味のわかりそうな文字を使ったほうがいい。むりやり_を仮引数名にしてもいいことはない。

['A','B','C'].each {|_| p _}

 Rubyでは_だと使わない捨てるべき変数であるかのように見えてしまう。パターンマッチではそういった意味で使われることがあるから。

 タイプ数を減らせるので番号指定パラメータを使うことには価値がある。けれど可読性が犠牲になるのは間違いない。トレードオフ

['A','B','C'].each {p _1}
['A','B','C'].each {|_| p _}
['A','B','C'].each {|v| p v}

所感

 相変わらず説明不足がすごい。

対象環境

$ uname -a
Linux raspberrypi 5.10.52-v7l+ #1441 SMP Tue Aug 3 18:11:56 BST 2021 armv7l GNU/Linux