Ruby風かつ高速と噂の言語Crystalを使ってみた

ちょっと興味を持ったので巷(?)で盛り上がっているCrystalを入れてみました.使ってみたというより触ってみた程度です.

Crystalとは

公式サイトによると,次のような特徴があるそうな.

  • Ruby風味の文法
  • 静的な型チェックがある
  • Cのコードが呼び出せる?
  • 効率的なネイティブコードへとコンパイルできる

インストール

githubからインストールページに飛べます.macだとHomebrewから以下のようにインストールができるみたいです.

brew tap manastech/crystal
brew update
brew install crystal-lang

Hello,world!

Crystalのファイル拡張子crだそうです.

puts "Hello, crystal!"

実行結果

$ crystal hello.cr
Hello, crystal!

crystaloコマンdでコンパイルと実行をやってくれるみたいですね. crystal run ファイル名でもできるそう.

また,コンパイルして実行ファイルを生成することもできます.Crystalの特徴はここですね.

$ crystal build hello.cr
$ ./hello

各種文法

コメント

#でコメントができます.

# This is a comment
puts "Hello, crystal!"   # => Hello, crystal!

以降rubyと同じところは飛ばしていきます

Integers・Float

精度指定ができる

Crystalには整数型がいくつかあり何も指定をしなければInt32Int64UInt64から適当なのが選ばれるみたいです.後ろにi8, i16, i32, i64などをつけることでそれぞれの精度で扱われます.iをuにすると符号なしとなります.

a = 2     
b = 2_i16   
c = 2_u64   
puts a.class    # => Int32
puts b.class    # => Int32
puts c.class    # => UInt64

Floatも同じです.

  1.0_f32  # Float32
  1_f32    # Float32

アンダースコア表記ができる

カンマの代わりに,変数をアンダースコアで繋げて書くことができます.見やすくなりますね.でもこんな大きな数使わないような

under_score_num = 111_222_333
puts under_score_num    # => 111222333

Char & String

Char型はシングルクオート',String型はダブルクオート"を使います.

'a' # char
"abc" #string

なのでこういうことはできません.

puts 'Hello, crystal'
$ crystal hello.cr
Syntax error in ./hello.cr:1: [1munterminated char literal, use double quotes for strings

puts 'Hello, crystal!'
      ^

Array

rubyと違って,配列内の型が何であるかに気を使わなければなりません.

a = [1, 2, 3]
b = [1, "Hello", 'w']
puts a.class    # => Array(Int32)
puts b.class# => Array((String | Int32 | Char))

x = [] of (Int32 | Char)
y = Array.new(String)

最初に中身のある配列を与えた場合はcrystalで勝手に型を与えてくれますが,配列の初期化を行う際には型の宣言が必要です.

x = [] of (Int32)
x << "hello" # => no overload matches 'Array(Int32)#<<' with types String

Tuple

Tuple型があります.pythonとかを使っている人には馴染みのものですね.

tuple = {1, "hello", 'x'}
puts tuple          # => {1, "hello", 'x'}
puts tuple.class    # => {Int32, String, Char}
puts tuple[1]      # => hello

実行速度

とまあ簡単なところを比較してみたところで,実行速度についても比較してみました.

rubyとcrystalで単にループを回しただけです.もっといい比較方法はあるよなきっと・・・.

プログラムはこんな感じ.rubyもほぼ同じような感じです.違うのは時間を呼び出すメソッドくらい.

# 比較用プログラム

start_at = Time.now.to_i * 1000 + Time.now.millisecond

n = ARGV[0].to_i
x = 0
y = 0

n.times do |i|
  x += 1
  y -= 1
end

end_at = Time.now.to_i * 1000 + Time.now.millisecond

puts x 
puts y

puts "crystal" + "-" * 23
puts "loop: #{n}"
puts "Time:#{end_at - start_at}"
puts "-" * 30

実行結果

ループ回数 ruby実行時間(msec) crystal実行時間(msec)
100000 7 1
1000000 77 3
10000000 810 27
100000000 8102 349

速いですね. ちなみにこれは純粋にループの前後の時間のため,コンパイルの時間などを考慮してはいないです.

それと,余談ですがCrystalではコマンドライン引数は--の後に書きます.こんな感じ.

crystal loop.cr -- 10000000

もっと色々利点や特徴があるのでしょうが,パッと比較できるのはこの程度.もう少し触ってみようかなあ.


追実験

もうちょっとましなコード?でやろうと思ってフィボナッチ数でもやってみました.コードと実験結果です.

# フィボナッチ数を求めるプログラム

def fibonacci(n)
  if n == 0
    return 0
  elsif n==1
    return 1
  else
    return fibonacci(n-1) + fibonacci(n-2)
  end
end

n = ARGV[0].to_i

start_at = Time.now.to_i * 1000 + Time.now.millisecond
fibonacci(n)
end_at = Time.now.to_i * 1000 + Time.now.millisecond

puts "crystal" + "-" * 23
puts "number: #{n}"
puts "Time:#{end_at - start_at}"
puts "-" * 30
n番目 ruby実行時間(msec) crystal実行時間(msec)
10 0 0
20 1 0
30 180 11
40 26267 1327

Written with StackEdit.