Cookpad Daily Ruby Puzzles Extraを解いた

RubyKaigi 2019 Cookpad Daily Ruby Puzzles の正解と解説 - クックパッド開発者ブログ

読んで面白かったので久しぶりにrubyを触ってExtraのパズル解いた。

Extra 1

Hello = "Hello"

# Hint: Stop the recursion.
def Hello
  Hello%() +
    " world"
end

puts Hello()

3-3と同様に()%()にして%記法にする。

リテラル (Ruby 2.6.0)

!の部分には改行を含めた任意の非英数字を使うことができます (%w、%W、%i、%I は区切りに空白、改行を用いるため、!の部分には使うことができません)。 始まりの区切り文字が括弧((',[',{',<')である時には、終りの区切り文字は 対応する括弧になります。括弧を区切り文字にした場合、対応が取れていれば 区切り文字と同じ括弧を要素に含めることができます。

Helloが1行目の宣言の変数に、%()が空文字列になるのはわかるけど、rubyはString並べると結合されんの……? 謎。

Extra 2

s = "Dž"
# Hint: https://techlife.cookpad.com/entry/2018/12/25/110240
s == s.upcase or
  s == s.downcase or puts "Hello world"

タイトルケース、そういうのがあるのか。勉強になります。upcaseでもdowncaseでもない第三のcase。

techlife.cookpad.com

完全に余談ですが、Unicode の大文字・小文字の話題になると、「Dz」という字の話をするのがお作法です。これは D と z の 2 文字ではなく、D と z が合体した 1 つの文字です。こういう文字を、digraph、二重音字と言います。この文字には、大文字・小文字に加え、タイトルケース(先頭の文字だけが大文字)の 3 種類があります。

Extra 3

def say
  s %= 'Small'
  t = 'world'
  puts "#{s} #{t}"
end

TracePoint.new(:line){|tp|
  tp.binding.local_variable_set(:s, 'Hello')
  tp.binding.local_variable_set(:t, 'Ruby')
  tp.disable
}.enable(target: method(:say))

say

結構時間かかった。s = 'Small'の前にTrasePointがs'Hello'をsetするので、s = 'Small'で上書きされそうになるのを、s %= 'Small'にしてやることで、Stringの%を使う自己代入にしてあげる。Stringの%はprintfと同じ規則でのフォーマットで、'Hello'にはフォーマット指定子が含まれないのでフォーマット後文字列は'Hello'のままとなる。合ってる?

もう一個の1文字回答なんなんだろう……。

感想

楽しかった。%が酷使されている気がする。

2019/4/26追記

Extra 3の別解分からないのでカンニングした。

blog.n-z.jp

def say\
  s = 'Small'
  t = 'world'
  puts "#{s} #{t}"
end

TracePoint.new(:line){|tp|
  tp.binding.local_variable_set(:s, 'Hello')
  tp.binding.local_variable_set(:t, 'Ruby')
  tp.disable
}.enable(target: method(:say))

say

\で行を繋げることで、def say s = 'Small'扱いにし、引数s(デフォルト値'Small')にしてあげることで、t = 'world'の行の評価前にsに'Hello'、tに'Ruby'がsetされて、評価後はs = 'Hello'、 t = 'world'になる。

バックスラッシュの行継続、メソッド定義での引数宣言の()が省略できる、引数のデフォルト値が代入と同じ形で表現できるためsayでの呼び出しで引数0でもエラーにならない、の三点。綺麗ですね。

docs.ruby-lang.org

プログラムは式を並べたものです。式と式の間はセミコロ ン(;)または改行で区切ります。ただし、バックスラッシュに続く改行は文 の区切りにならず、次の行へ継続します。