Rubyと違いのあるCrystalの文字列の注意点
最初に
Crystal と Ruby はよく似た言語だが、様々な点で違いがある。
Ruby から Crystal の文字列を扱うときの注意点を書いてみる。
Crystalには文字がある
Ruby には文字列という概念しかないが、Crystal には文字と文字列という概念がある。
C言語などを学習したことがある人にとっては馴染み深いだろうが、
文字と文字列で区別するとき、
- 文字 …… 1文字で
'
で囲む。 - 文字列 …… 0文字以上で
"
で囲む。
という違いがある。
Ruby の文字列をシングルクォートで書いてると、Crystal に移植するとき、"
に変更する必要があって面倒。
Crystalの文字列はイミュータブル
Ruby の文字列は、ミュータブルなもので、破壊的メソッドで破壊的な変更をすることができる。
しかし、Crystalの文字列は、イミュータブルで、破壊的メソッドはない。
イミュータブルにすると速くなることがあったり、コードとしての安全性が増す等のメリットがあるようだ。
なお、Python や JavaScript などの言語で、文字列はイミュータブルものである。
Ruby も昔は、Ruby 3.0では文字列はイミュータブルなものに変更しようという動きがあったが、
結局、後方互換性に大きな問題があるということでなくなった。
なお、後方互換性のない大きな変更をしたところで、大きなメリットがないという批判もあった。
具体的にできないこと
str = "Hello!" p str[0] # => 'H' p str[2..3] # => "ll"
Crystal は[]
で文字や文字列をとることができるが、
Crystalの文字列はイミュータブルなので[]=
の形で文字列の中の文字を変更することができない。
Crystalの文字と文字列の結合
文字と文字列は、足すことができる。
p "Hello" + '!' # => "Hello!" p '!' + "Hello" # => "!Hello"
しかし、文字と文字は足すことが、できない。
p '!' + '?' # Error: no overload matches 'Char#+' with type Char # # Overloads are: # - Char#+(str : String) # - Char#+(other : Int)
このエラー文を見てわかるが、文字にはInt
型の数値を足すことができる。
p 'b' + 1 # => 'c' p 'b' - 1 # => 'a'
これは、Ruby にはない機能だ。競プロとかだと、ちょっと便利かもしれない。
Crystalの文字と文字列の比較
p 'a' == "a" # => false p 'a' == 97 # => false p 'a'.ord # => 97
Crystal の文字と文字列は足すことができたが、同じだろうと思うものでも比較をするとfalse
を返してくる。
このあたりは非自明で、注意ポイントだと捉えている。
Crystal の文字は、倍にできない。
RubyでもCrystalでも、文字列は倍などにできる。
p "a" * 2 # => "aa"
しかし、Crystalの文字に対して、*
を使えないので注意が必要。
'a' * 2 # error in line 1 # Error: undefined method '*' for Char
実行しようとすれば、定義されてないので、Charに対して*
は未定義というエラーで怒られる。
String#chars
Ruby のString#chars
は、文字列から文字の配列を作るだけでなく、ブロックで文字を回すこともできる。
しかし、CrystalのString#chars
は、文字の配列を作るだけで、ブロックを取ることはできない。
"str".chars{ |c| p c } # Error: 'String#chars' is not expected to be invoked with a block, but a block was given
Ruby でも、 Crystal でも、文字列の文字を回したくなったら、each_char
を使おう。
"str".each_char{ |c| p c }
Crystal の全体的な方針としてエイリアスは作らない方針があり、ブロックをとって回すメソッドはeach_
を接頭辞(prefix)でつけると考えてよい。
なお、Crystal のchars
もeach_char
も、要素は文字列Stgring
ではなく、文字Char
となることに注意。
p typeof("str".chars) # => Array(Char)
String#bytes
Ruby の String#bytes
は、文字列からバイトの配列を作るだけでなく、ブロックでバイトを回すことができる。
こちらもchars
同様、Crystal ではbytes
でブロックをとって回すことはできない。
ブロックをとって回すときは、each_byte
を使う。
Ruby でも、chars
やbytes
でブロックをとって回すのは少しお行儀が悪いので、each_char
やeach_byte
を使いましょうという話。
また、Crystal の返す配列の要素は、Int32
などではなく、Int8
型である点に注意。
Crystalは、三単現
文字列に限った話ではないが、Crystal は三単現(三人称・単数形・現在形)で統一する方向性である。
Ruby でのString#start_with?
やString#end_with?
は、CrystalではString#starts_with?
やString#ends_with?
である。
JavaScript でも同名の三単現のメソッドがあるので、JavaScript
使いは馴染みやすそうである。
最後に
ザーッと Ruby から Crystal の文字列を扱うときの注意点を書いてみたが、いかがでしたでしょうか?