70974


Ruby2.7.1を使用しています。

`.`を使用した際の動作の違いについて、教えて頂けないでしょうか？  
具体的には、以下のような演算の際に異なる結果が出力されます。

[code]

    1/0  + 1              # => ZeroDivisionError (divided by 0)
    1./0 + 1              # => 1
    1./0 + 1 + 1./0 + 1   # => SyntaxError
    
[/code]

[Rubyのリファレンスマニュアル](https://docs.ruby-lang.org/ja/latest/doc/index.html)
を確認したり、検索したのですが言及した記事を見つけられず...  
よろしくお願いします。


CC BY-SA 4.0
2020-10-03T14:03:09.333
70901
2020-10-07T01:55:29.790
2020-10-04T06:24:00.497
3060
39723
question
ruby
1/0 + 1 と 1./0 + 1 の演算結果が異なるのはなぜ？


Rubyの [浮動小数点数値リテラル](https://docs.ruby-
lang.org/ja/latest/doc/spec=2fliteral.html#num)
では、小数点以下（`.`の右側）の数字記述も必須となっているようです。形式的な言語仕様定義は見つけられませんでしたが、少なくとも実際にそのように動作するようです。

> `123.45`  
>  浮動小数点数。 `.1` など "`.`" で始まる浮動小数点数は許されなくなりました。`0.1` と書く必要があります。
[code]

    puts 1    # => 1
    puts 1.0  # => 1.0
    puts 1.   # => syntax error
    
[/code]

<https://github.com/ruby/ruby/blob/ruby_2_7/parse.y#L8214-L8226>  
Ruby処理系の`parse_numeric`実装を見る限り、数字列＋`.`に続く文字(`nextc`)が数字以外(`!ISDIGIT`)の場合は、`.`を未解析に戻した(`pushback`)うえで数字列部分のみを整数の数値リテラルとして解釈するようです。

* * *

>
[code]

>     1./0 + 1  # => 1
>  
[/code]

上記は、`1`(整数リテラル) `.`(メソッド呼び出し) `/`(メソッド名) `0 + 1`(実引数の部分式)と解釈されているようです。

<https://wandbox.org/permlink/CnPrmPCx72FiTErr>

[code]

    require 'ripper'
    require 'pp'
    
    pp Ripper.sexp('1./0 + 1')
    # [:program,
    #  [[:command_call,
    #    [:@int, "1", [1, 0]],
    #    [:@period, ".", [1, 1]],
    #    [:@op, "/", [1, 2]],
    #    [:args_add_block,
    #     [[:binary, [:@int, "0", [1, 3]], :+, [:@int, "1", [1, 7]]]],
    #     false]]]]
    
    pp Ripper.sexp('1.0/0 + 1')
    # [:program,
    #  [[:binary,
    #    [:binary, [:@float, "1.0", [1, 0]], :/, [:@int, "0", [1, 4]]],
    #    :+,
    #    [:@int, "1", [1, 8]]]]]
    
[/code]

* * *

>
[code]

>     1./0 + 1 + 1./0 + 1   # => SyntaxError
>  
[/code]

上記が構文エラーとなる理由ははっきりと分かりませんが、括弧`()`でAST構造を明示すると通るパターンはあるようです。（おそらく
[ruby/parse.y](https://github.com/ruby/ruby/blob/master/parse.y)
を解析しないと説明不可能。）

<https://wandbox.org/permlink/ohMUudWTEsyBO8c3>

[code]

    require 'ripper'
    require 'pp'
    
    pp Ripper.sexp('1./0 + 1 + 1./0 + 1')    # nil
    pp Ripper.sexp('1./0 + 1 + 1./(0 + 1)')  # OK
    pp Ripper.sexp('1./0 + 1 + (1./0 + 1)')  # OK
    pp Ripper.sexp('1./0 + (1 + 1./0 + 1)')  # nil
    
[/code]


CC BY-SA 4.0
2020-10-07T00:57:10.600
70974
2020-10-07T01:55:29.790
2020-10-07T01:55:29.790
49
49
70901
answer