Rubyのエラーをちょっと整理(初級者向け)

 Rubyのエラーをちょっと整理(初級者向け)

Aa2ed090fd893f58f4d139c3285106bc?s=128

Y. Sato ( ****)

October 24, 2020
Tweet

Transcript

  1. Rubyのエラーをちょっと整理(初級者向け) Rubyのエラー, エラー⽂をちょっと⾒てみます。 @universato Rubyのエラーをちょっと整理(初級者向け) 2020/10/24 1

  2. ⾃⼰紹介 universato(ユニヴァーサトウ)ことユニです。 ⼀定の範囲で、好きによんで下さい。 Rubyのエラーをちょっと整理(初級者向け) 2020/10/24 2

  3. 最初に スライドの枚数が多く、発表では⾶ばすページも多いですが、 別の場所に置くので、暇なときにゆっくり⾒ていって下さい。 検証︓Ruby 2.7.1 ※2ヶ⽉後の年内に、バージョン3.0がでる予定。 Rubyのエラーをちょっと整理(初級者向け) 2020/10/24 3

  4. ザックリした⽬次 エラーの分類 構⽂エラー(主なsyntax error, Hashとブロック、8進数) 実⾏時エラー ArgumentError, TypeError 参考記事・参考書籍 Rubyのエラーをちょっと整理(初級者向け)

    2020/10/24 4
  5. エラーの分類 ⼀般なエラーの段階別の分類 構⽂エラー = シンタックスエラー(Syntax error) 実⾏時エラー = ランタイムエラー(Runtime error)

    ザックリいうと、 構⽂エラーは、⽂法=構⽂(syntax)が正しくないコードが実⾏できない。 実⾏時エラーは、実⾏中に起きるエラー。 Rubyのエラーをちょっと整理(初級者向け) 2020/10/24 5
  6. 構⽂エラー = シンタックスエラー 構⽂・⽂法(stntax)が正しくなくプログラムが実⾏できないエラー。 原則、構⽂エラーがあると、プログラムが実⾏されません。 Rubyのエラーをちょっと整理(初級者向け) 2020/10/24 6

  7. Rubyでの構⽂エラー = シンタックスエラーのエラー Rubyでの主なシンタックスエラー ブロックが、⾜りなかったり、対応できていない。 end が⾜りない。 波括弧 {} が対応できていない。

    配列やハッシュの中にカンマに過不⾜がある。 変なところに記号や予約語がある。 ハッシュをブロックと勘違いされる。 解釈できない数値がある。ex) 0o80 Rubyのエラーをちょっと整理(初級者向け) 2020/10/24 7
  8. 構⽂エラーの例: ブロックの終わりの } を書き忘れる Ruby (1..3).each { |i| puts i

    ↓ Main.rb:2: syntax error, unexpected end-of-input, expecting '}' end-of-input はわかりにくいですが、「実⾏してるファイルの終わり」の意味。 全体を訳すと「Main.rbというファイルの2⾏⽬で、シンタックスエラーです︕ 予期せ ぬファイルの終端です、 } を期待していたのに」という意味。 Rubyのエラーをちょっと整理(初級者向け) 2020/10/24 8
  9. Rubyの構⽂エラーの例: 変なところに記号や予約語がある = ↓ Main.rb:1: syntax error, unexpected '=' (訳:

    構⽂エラー、予期せぬ`=`) 変なところに記号や予約語があることによるエラーです。 記号1⽂字だけでもエラーの⽂が全然違ったりするので、試してみると⾯⽩いかもしれ ないです。例) @ , + , ' , / , % , = , : , ! Rubyのエラーをちょっと整理(初級者向け) 2020/10/24 9
  10. Rubyの構⽂エラーの例: doと|i|の位置を間違える 慣れてない頃、よくこうやって順番を間違えていた記憶 5.times |i| do end ↓ Main.rb:1: syntax

    error, unexpected `do' (訳: 構⽂エラー、予期せぬ`do`) 5.times |i| do ^~ 「unexexpected `|'」と表⽰されるべきな気がするが、「do」がでる。 エラーの箇所はわかるけど、エラー内容はちょっとわかりにくい気がする。 Rubyのエラーをちょっと整理(初級者向け) 2020/10/24 10
  11. Rubyの構⽂エラーの例: カンマが過剰 [,] ↓ Main.rb:1: syntax error, unexpected ',', expecting

    ']' 要素がないのに , が急に現れて、予期せぬ , となっている。 無駄なカンマがあるときに⾒られるエラー。 Rubyのエラーをちょっと整理(初級者向け) 2020/10/24 11
  12. Rubyの構⽂エラーの例: カンマが不⾜ p [1 2] ↓ Main.rb:1: syntax error, unexpected

    integer literal, expecting ']' p [1 2] ^ 要素の間の , が不⾜していて、予期せぬ整数(integer)があることによるエラー。 【余談】⽂字列の間に空⽩があっても、1つの⽂字列になります(何故?) "Hello, " "world!" #=> "Hello, world!" Rubyのエラーをちょっと整理(初級者向け) 2020/10/24 12
  13. Rubyの構⽂エラーの例: ⽂字列の囲い込み忘れ p 'Hello, world! ↓ Main.rb:1: unterminated string meets

    end of file p 'Hello, world! ⽂字列を囲み忘れたことによるエラー。 syntax errorだけど、エラー⽂にsyntax errorと表⽰されないパターン. エラー⽂を直訳すると「終了のない⽂字列が、ファイルの終わりに交わった」(?) Rubyのエラーをちょっと整理(初級者向け) 2020/10/24 13
  14. Rubyの構⽂エラーの例: ハッシュをブロックと勘違いされる これは、ちょっと有名なやつです。 p {"Ruby" => 1993, "C" => 1972}

    ↓ Main.rb:1: syntax error, unexpected =>, expecting '}' p {"Ruby" => 1993, "C" => 1972} ^~ ハッシュのつもりで {} を書いても、 ブロックの {} と勘違いされてエラーになります。 ※ p({~}) と引数を丸括弧で囲めば、ちゃんと動きます。 Rubyのエラーをちょっと整理(初級者向け) 2020/10/24 14
  15. 8進数に関するシンタックスエラー p 0o10 #=> 8 p 010 #=> 8 p

    080 # Invalid octal digit (訳: 有効でない8進数) Main.rb:3: Invalid octal digit p 080 ^~ Rubyでは、 0 始まりや 0o 始まりの数値は、8進数として解釈されます。 8進数は 0 〜 7 の数値で構成されるので、 8 や 9 があるとエラーになります。 ※多くの他⾔語でも、 0 か 0o のどちらかをサポートしています。 Rubyのエラーをちょっと整理(初級者向け) 2020/10/24 15
  16. File::Stat#modeの数値は8進数(lsコマンドの内部の数値) RubyのFile::Stat#modeで返ってくる数字について (by yupa-san) inode(7) - Linux manual page S_IFSOCK

    0140000 socket # s S_IFLNK 0120000 symbolic link # l S_IFREG 0100000 regular file # - S_IFBLK 0060000 block device # b S_IFDIR 0040000 directory # d S_IFCHR 0020000 character device # c S_IFIFO 0010000 FIFO # p 左から、1桁⽬が「8進数であることを⽰す0」で、2, 3桁⽬がファイルのタイプ、その 次が特別なパーミッションで、残り3桁が普通のパーミッション。 Rubyのエラーをちょっと整理(初級者向け) 2020/10/24 16
  17. Ruby以外の8進数リテラルの扱い C, Java, Python 2 …… 0 始まりだと、8進数。 Rust, Swift,

    Crystal, Python 3 …… 0o と書く必要あり。 Crystal, Python 3 …… 0 始まりだと、sytanx error。 Rust, Swift …… 0 始まりだと、10進数。 JavaScript …… 0o も 0 も書けるが、 0 始まりの処理が特殊。 8 か 9 が含まれていると10進数となり、 0 〜 7 のみだと8進数になる。 昔からあるメジャーな⾔語CやJavaでは 0 始まりは8進数でしたが、わかりにくいし古 い表記といえそうなので、 0o 始まりで書いた⽅が良さそうです。 Rubyのエラーをちょっと整理(初級者向け) 2020/10/24 17
  18. Rubyの2進数のエラーが難しい 8進数に8や9が混ざると「 Invalid octal digit (訳: 有効でない8進数)」とわかりやすいエ ラー⽂がでるのに、2進数は異なっていて難しいです。 p 0b20101

    ↓ Main.rb:1: numeric literal without digits p 0b20101 ^~ Main.rb:1: syntax error, unexpected integer literal, expecting end-of-input p 0b20101 ^~~~~ エラー⽂を訳すと「 0b は数字のない数値リテラルだ」と「構⽂エラー︓予期せぬ数値 リテラル 20101 だ、ファイルの終わりを期待してたのに」となる。 Rubyのエラーをちょっと整理(初級者向け) 2020/10/24 18
  19. 実⾏時エラー = ランタイムエラー 構⽂エラーがなくコードが実⾏され始めて、実⾏中に起きるエラー。 実⾏時に起きるRubyの代表的なエラー(例外)のクラスを少し紹介していきます。 ArgumentError , TypeError Rubyのエラーをちょっと整理(初級者向け) 2020/10/24

    19
  20. ArgumentError と TypeError この2つは、メソッドの引数が誤っているときの代表的なエラー。 ⼀般に、Argumentは「引数」、Typeは「型」で、よく使われる専⾨⽤語です。 なお、Rubyのこの⽂脈では、Typeは「型」というより「クラス」。 ArgumentError 引数の数が異なるときのエラー。 引数の数にもクラスにも問題ないが、期待しない性質のときのエラー。 その他:

    引数がないパターン等 TypeError 引数の数は同じだけど、期待しないクラスのときのエラー。 その他: 引数がないパターン等 上記のような傾向があります。 Rubyのエラーをちょっと整理(初級者向け) 2020/10/24 20
  21. ArgumentError 主に2種類のエラーに⼤別されます。 引数の数が異なるパターン 引数の数にもクラスにも問題ないが、期待しない性質だったとき。 引数のクラスが異なるときは TypeError と別個の括りなのに、 ArgumentError に2種類のエラーがあるのは違和感……。 Rubyのエラーをちょっと整理(初級者向け)

    2020/10/24 21
  22. ArgumentError : i) 引数が異なるとき エラー⽂が読めれば、ほぼ簡単に修正できるエラーです。 p 1024.to_s #=> "1024" p

    1024.to_s(2) #=> "10000000000" p 1024.to_s(:delimited, locale: :fr) #=> ArgumentError ※Railsでは to_s が拡張され桁区切りを指定できますが、素のRubyは出来ません。 ↓ Main.rb:3:in `to_s': wrong number of arguments (given 2, expected 0..1) (ArgumentError) from Main.rb:3:in `<main>' given 2, expected 0..1 は「2つの引数が与えられたけど、期待した個数は0~1個」の 意味ですね。 Rubyのエラーをちょっと整理(初級者向け) 2020/10/24 22
  23. 【余談】引数の数の違うときのエラー⽂の歴史 p 1024.to_s(:delimited, locale: :fr) #=> ArgumentError ↓ Before (Ruby

    〜2.2) wrong number of arguments (2 for 0..1) (ArgumentError) After (Ruby 2.3 〜 2.7) wrong number of arguments (given 2, expected 0..1) (ArgumentError) エラー⽂、だいぶわかりやすくなっている。 Rubyのエラーをちょっと整理(初級者向け) 2020/10/24 23
  24. ArgumentError : ii) 引数の数・クラス以外の性質に問題があるとき p [0] * 3 #=> [0,

    0, 0] p [0] * -1 # negative argument (ArgumentError) Main.rb:2:in `*': negative argument (ArgumentError) from Main.rb:2:in `<main>' プログラミング⾔語の中でも珍しい部類ですが、Rubyの演算⼦はメソッド。 Array (配列)のインスタンスメソッド * は引数に Integer を1つとって、 その引数の数だけ配列のサイズを倍にします。 Rubyのエラーをちょっと整理(初級者向け) 2020/10/24 24
  25. TypeError 主なパターンは、 引数の数に問題はないが、引数のクラスが異なるとき。 Rubyのエラーをちょっと整理(初級者向け) 2020/10/24 25

  26. TypeError i) String#+ "1" + 1 ↓ Main.rb:1:in `+': no

    implicit conversion of Integer into String (TypeError) String クラスのメソッド + は、⽂字列どうしを結合する⽬的で、あるべき引数は String クラスです。しかし、実際の引数は Integer の 1 で、内部で Integer を String に変換できるか検証され「 Integer から String への暗黙の変換はできない」 と⾔われています。 【余談】このケースでは、引数側に⽂字列に変換する to_str が実装されていれば、内 部で暗黙の変換を⾏われます。 Rubyのエラーをちょっと整理(初級者向け) 2020/10/24 26
  27. TypeError ii) integer#+ 1 + "1" ↓ Main.rb:1:in `+': String

    can't be coerced into Integer (TypeError) from Main.rb:1:in `<main>' Integer と String を逆にすると、エラー⽂に聞き慣れない単語"coerced"がでてきま す。「 String は Integer に強制(的に変換)できなかった」みたいな意味。 マニアックな話ですが、数値クラスは異なるクラスの演算を⾏うときは、内部で coerce というメソッドを⽤いクラスを統⼀してから演算しています。 String クラス に coerce メソッドを実装してあげれば、エラーは解決します(!?) Rubyのエラーをちょっと整理(初級者向け) 2020/10/24 27
  28. ArgumentError , TypeError は、いい加減? Ruby の例外クラスは分類が粗すぎる or 細かすぎる - まめめも

    (2009/4/23) 実際、TypeError と ArgumentError の区別ってかなりいい加減に決められている ように感じます。 a = []; a << a a.flatten #=> tried to flatten recursive array (ArgumentError) (1...5.0).max #=> cannot exclude non Integer end value (TypeError) このように ArgumentError , TypeError は、引数がなくても⽣じます。 Rubyのエラーをちょっと整理(初級者向け) 2020/10/24 28
  29. 最後に ご静聴ありがとうございました。 間違いがあったら教えて下さると嬉しいです。 Rubyのエラーをちょっと整理(初級者向け) 2020/10/24 29

  30. 参考記事 - Rubyリファレンスマニュアル(通称るりま) Rubyリファレンスマニュアル ArgumentError (Ruby リファレンスマニュアル) TypeError (Ruby リファレンスマニュアル)

    Rubyのエラーをちょっと整理(初級者向け) 2020/10/24 30
  31. 参考記事・参考書籍 『Ruby 超⼊⾨』 2-7「エラーメッセージを読み解く」 『プロを⽬指すためのRuby⼊⾨』(通称: チェリー本 ) 第11章 Ruby の例外クラスは分類が粗すぎる

    or 細かすぎる - まめめも Ruby のエラーメッセージを読み解く(初⼼者向け)その 1 - Qiita Ruby のエラーメッセージを読み解く(初⼼者向け)その 2 - Qiita どの8進数リテラルが使えるかはプログラミング⾔語によって異なる - Qiita Rubyのエラーをちょっと整理(初級者向け) 2020/10/24 31
  32. その他 本スライドは、Marp Nextで作成しました。 残りのページのスライドは、検証⽤などのメモです。 Rubyのエラーをちょっと整理(初級者向け) 2020/10/24 32

  33. JavaScriptの0始まりの8進数の不思議な挙動 JavaScript Node.js v12.18.3 console.log(080); // 80 console.log(010); // 8

    console.log(0o10); // 8 console.log(0O10); // 8 console.log(0o10); // SyntaxError: Invalid or unexpected token JavaScriptは 0 始まりの処理が特殊。 0 始まりでも 8 か 9 が含まれているときは10進数 0 始まりで 0 〜 7 のみで構成されるときは8進数。 0o 始まりだと、必ず8進数で、 8 などがあると構⽂エラー。 Rubyのエラーをちょっと整理(初級者向け) 2020/10/24 33
  34. Pythonの8進数の挙動 Python 3.8.2 print(0o10) # 8 print(0O10) # 8 print(010)

    # SyntaxError: leading zeros in decimal integer literals # are not permitted; use an 0o prefix for octal integers Pythonは、2 -> 3にかけて 010 のような 0 始まりの8進数を構⽂レベルで禁⽌した。 Rubyのエラーをちょっと整理(初級者向け) 2020/10/24 34
  35. RustやSwiftでは、 0 始まりは10進数の扱い Rust fn main(){ let n = 010;

    println!("{}", n); } print(010) // 010 Rubyのエラーをちょっと整理(初級者向け) 2020/10/24 35
  36. 0始まりで書きたくなる状況は、⽂字列の⽅が良さそう birthday = 0107 breakfast = 0730 tel = 0120_0000_0000

    post_code = 010_0000 こういう電話番号や郵便番号は、0始まりがあったり、計算するものでもなかったり、 様々な理由により、⽂字列で管理した⽅が良いと思われる。 【参考】invalid octal digit エラーが起きたとき - Qiita datetime: DateTime.new(2015,10,08,00,00,00) Rubyのエラーをちょっと整理(初級者向け) 2020/10/24 36
  37. numeric literal without digits Bug #2407: numeric literal without digits

    - Ruby master - Ruby Issue Tracking System 0o_, 0x_ などprefixの直後に _ が来た場合には、このエラーを意 図的に出しています。_というのは数値表現の数字と数字の間に⼊る もので、prefixと数字の間に⼊るものではない、と考えたからです。 「エラーメッセージがわかりにくい」とか「prefixと数値の間に _ が⼊って何が悪い」などのいう指摘は、今後考慮しないとは⾔いま せんが、少なくとも現時点ではこれは仕様です。 10年以上前の2009年のMatz⽒の発⾔。 0o_101 の⽅がわかりやすいような? ⾃分は書く機会がないけど、機会があったらハマる気がする。 Rubyのエラーをちょっと整理(初級者向け) 2020/10/24 37
  38. バッククォートとシングルクォートからなる引⽤符 Rubyのエラー⽂を⾒てると、引⽤符としてバッククォートとシングルクォートで囲ま れた処理が⾏われる。LATEXなどでは引⽤符は `hoge' と囲むもので、 ❛hoge❜ と表⽰し てくれるとのこと。もし ❜hoge❜ だと変ですよね。逆に、開始と終了で同じ記号を使う

    のは、プログラマの怠惰な⽅法なのだとか(?)。 Rubyのエラーをちょっと整理(初級者向け) 2020/10/24 38