流行っていなくとも、良いものは良い。美しいものは美しい。

Nimro.png

『Nimを触り始め(る|た)けどドキュメント少なすぎ! なにから学べばいいかわからない!』
という人(過去の私)へ贈ります。

「このあたり知っておけば、言語理解が速いはず :kissing_heart: 」 って想いで書いています。
触り始めて間もない私なので粗があると思いますが、みなさんにとってNimへの良い入り口となれば幸いです:blush:

ちなみに私はWeb系に偏っており、ポインタ?美味しいよねぇ といった具合です。
詳しい記事への導線を配置しているので、この記事はあくまでも最初のつまづき用の杖だと思ってください。

Nimの素敵ポイント

いきなり本題とずれますが、宣伝させてください。 :tired_face:

Nim とは
美しくありながら
パフォーマンスがかなり高く
メタプログラミングに長けた
コンパイル言語です。

美しく、そして速い言語、Nim
const
  fizzBuzz = "FizzBuzz\n"
  fizz     = "Fizz\n"
  buzz     = "Buzz\n"

var str = ""

for i in 1 .. 100:
  if   i mod 15 == 0: str.add(fizzBuzz)
  elif i mod  3 == 0: str.add(fizz)
  elif i mod  5 == 0: str.add(buzz)
  else:
    str.add($i)
    str.add("\n")

stdout.write(str)

『じゃあ、何故流行ってないの?』
その大きな原因は ドキュメントの不足 だと思っています。
(公式というより、Qiitaとかブログとかの!)
みんなで盛り上げていけば輝く原石です。

こちらの記事も読んでいただけると、Nimの輪郭がはっきりすると思います。
Nim言語感想&概説

環境作りや実行周り

anyenvにはまだ居ません。(2018/01/17 時点)
Nimのバージョン管理 :point_right: choosenim

インストール(macOS)
brew install nim

公式サイトからのインストール :point_right: https://nim-lang.org/install.html

主流のライブラリマネージャーは nimble です。

エディタはVSCodeかKDevelop(IDE)でいいと思います。
VisualStudioCodeによるNim開発環境
Aporiaはちょっと使いづらい...。(AporiaはNimで書かれたIDE)

コンパイルと実行
nim c -r hello.nim
リリース向けコンパイル
nim c -d:release hello.nim

-d:releaseを付けないでコンパイルするとデバッグ情報が付加されるため、動作が遅くなります。

コードの書き方/読み方

基本的な構文はこちらの記事を。
- Syntax of Nim
- Nim Tutorial Part Iを日本語訳してみた(前編)
- Nim Tutorial Part Iを日本語訳してみた(後編)
- 【Nim】個人的逆引きリファレンス
最高!ほんと助かります :sob:

どんな関数があるの?ってときはソースコードを読むのがオススメです。
私は日本語ドキュメント依存のゆるふわエンジニア生活を送っていたために少々つらかったですが、関数内部の処理まで覗けるのは良いですね :crown:
私が最初のうちによく読んだのは lib/system.nim です。

あとは公式ドキュメントの Index⌘ + Fするとか :rolling_eyes:

キャメル? スネーク?

キャメルケース、スネークケースどちらでも書けます。たとえ宣言済みの変数であっても :flushed:
でも、公式ではキャメルケースが推奨されています。じゃあスネーク禁止にしたほうがよくない!?
Nim Enhancement Proposal #1 - Standard Library Style Guide
UpperCamel(Pascal) ↔ lowerCamel はエラーになります。

キャメル
readLine(stdin)
スネーク
read_line(stdin)
フィーバー!!(動きます)
r_E_a_D_l_I_n_E(stdin)

デバッグ

repr関数で値の中身を確認することができます。
JavaScriptのconsole.logとか、PHPのvar_dumpみたいな感覚で使ってます。
ポインタの参照先やアドレスなども確認できます。

出力はechoとかで行う
echo repr(holyGrail)

デバッグ向きechoとしてdebugEchoがありますが、
プロシージャ(関数)の副作用...?
コメント読んでも何を言ってるのかわかりませんでした :tired_face:
(デバッグ時にのみechoするのかな)

ステップ実行はこちらの記事を読んでいただけると。
nimのデバッグ
(私は試してないです)

変数/定数の宣言

宣言は三種類あります。

const
定数です。コンパイル時に値がわかるものでないといけません。
例えば、コンソールから入力されるような値は代入できません。この場合はletを使います。

var
他言語でもよく見る、普通の変数です。

let
再代入が禁止されたvarです。コンパイル時に値が決まっている必要はありません。

この項目は他の方もまとめられているのですが、本記事を読み進められるよう、一応書きました!
@2vgさん、アドバイスありがとうございました:v:

Stringへの型変換

型変換は様々ですが、Stringにできそうな大抵の型は$がサポートされていて、手前に付けると文字列になります。

var number = 42

echo "答えは" & $number & "です。"

&は文字列連結です。

echo関数では内部的に$が使われる上に、複数の引数を受け付けるので、以下のようにも書けます。

var number = 42

echo "答えは", number, "です。"

関数(proc, プロシージャ)への引数渡し

第一引数を関数の手前に書いてメソッドチェーン風にしたり、カッコを省略できたりします。これ好き :relaxed:
write関数を例に挙げます。

ノーマル
write(stdout, str)
チェーン
stdout.write(str)
カッコ省略
stdout.write str

write stdout, str
stdout.write"これもOK"

ネスト

letvar等はネストでまとめることができます。

ノーマル
const wrong   = "ポピプテピック"
const correct = "ポプテピピック"
var message: string
ネスト
const
  wrong   = "ポピプテピック"
  correct = "ボブネミミッミ"
var
  message: string

また、Nimでは主に2スペースのインデントが使われます。NimとPythonを交互に書くと酔いますね :fearful:

破壊的な関数

Nimに限った話では無いですが、再代入が禁止されている const let に対して破壊的な(変数の内容を書き換える)関数を使うとエラーになります。

string型にstring型を追加するadd関数の例です。

第一引数はvar限定
proc add(x: var string, y: string)
OK
var cuteVoice = "piyo"

cuteVoice.add("piyopiyo")
エラー
let cuteVoice = "piyo"

cuteVoice.add("gieeeeeeeeeeee!!!")

Nimの本

コメントにて、Nimの本を教えていただきました。
公式サイトに載ってたのにガンスルーしてました... :rolling_eyes:
LiveBook形式ならブラウザで開くことで、Google翻訳できるみたいです!

:point_right: Nim in Action

@6inさん、ありがとうございます :relaxed:

頂いた質問への回答

FizzBuzzのイミュータブルな解決法は?

現バージョンでforEachの実装が見当たらなかったため、このような書き方になるかと思います。
配列に足していく書き方をすると、パフォーマンスが結構落ちるかも。
Nimmerの先輩方!もっといい方法あれば、教えていただけると :pray:

import sequtils

proc fizzBuzz(i: int): string =
  if   i mod 15 == 0: "FizzBuzz"
  elif i mod  3 == 0: "Fizz"
  elif i mod  5 == 0: "Buzz"
  else: $i

for word in toSeq(1..100).map(fizzBuzz):
  echo word

@biotist さんにイテレータ版を教えていただきました!ありがとうございます!

iterator fizzBuzz(max: int): string =
  for i in countup(1, max):
    yield if   i mod 15 == 0: "FizzBuzz"
          elif i mod  3 == 0: "Fizz"
          elif i mod  5 == 0: "Buzz"
          else: $i

for word in fizzBuzz 100:
  echo word

パターンマッチはある?

case文はありますが、arrayやtupleなど、複数の値を抱えるものを同時に検査することはできません。(たぶん...)
ですので、FizzBuzzに使うと結構泥臭いことになります。

proc fizzBuzz(i: int): string =
  case i mod 15:
    of 3, 6, 9, 12: "Fizz"
    of 5, 10: "Buzz"
    of 0: "FizzBuzz"
    else: $i

改善が望まれます :innocent:
(これで洗練されている説もあります)

string interpolation はある?

string interpolation = 文字列補間
strutils というライブラリで%を使って実現できます。

import strutils

echo "答えは $answer です。" % ["answer", $42]

# => 答えは 42 です。

文字列内での変数展開のような実装は見つけることができませんでした。
これもあるといいなぁって思います。

Nim 0.18.0 でstrformatが追加されました :star2:
Nim 0.18.0のリリースまとめ #strformatモジュール
Module strformat

import strformat

const name = "りがに"

echo "今日のごはんは?"
echo fmt"わぁい {name}{readLine(stdin)}すこ"

おまけ

原型を保ったまま速度を詰めたFizzBuzz(100万回転)を置いておきます :japanese_goblin:
Nimのパフォーマンスの高さを体感していただければと思います。

var
  str = ""
  i = uint(0)

while uint(1_000_000) > i:
  i.inc
  case i mod 15
  of 3, 6, 9, 12:
    str.add("Fizz\n")
  of 5, 10:
    str.add("Buzz\n")
  of 0:
    str.add("FizzBuzz\n")
  else:
    str.add($i)
    str.add("\n")

stdout.write(str)

コンパイル時に全展開とかはナシで、もっと速いものがあれば教えてください!(懇願)

おわりの言葉

記事最初の画像のマスコット(Nimro)は私の落書きなので、非公式です。流行って!

「最初の頃、このへんでハマったよ」みたいなものがあれば、教えてください :raised_hand:

また、Nimはdevelブランチが活発で、将来この記事に書いてある幾らかは嘘っぱちになることも有り得ますので、お気を付けください :alien:
(嘘っぱちになったら ご指摘いただけると最高な気持ちです)

追記: NimroをSVG化したので置いておきます(Rejectされました:ghost:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 177.9 186">
  <style>
    .body { fill: #171921 }
    .eye,
    .mouth { fill: #fdfdfd }
    .iris { fill: #9a8700 }
    .tie,
    .crown { fill: #ffe952 }
    .jewelry.purple { fill: #bd93f9 }
    .jewelry.pink { fill: #ff79c6 }
    .jewelry.blue { fill: #6272a4 }
  </style>
  <path class="body" d="M28.9 72.5c0.1-2.3 4.3 1.5 16.3-3.8 6.2-2.7 16.4-10.5 20.7-17.6 8.7-14.3 18.3-23.9 42.1-25.5 19.1-1.3 36.7 9.4 43.8 22.3 6.6 11.9 10.2 21.2 14.8 27.1 5.7 7.2 13.2 4.3 10.8 9.1 -3 6.2-22.3 2.6-22.3 2.6 -5.9 30.6-9.8 47-33.7 65.1l1.1 22.5c0 1 0.8 1.8 1.7 1.9 3.5 0.5 11.6-0.3 10.6 3.7 -1.2 5.3-10.8 2.7-15.1 2.4 -1.1-0.1-2.1-0.9-2.4-2 -1.6-6.7-4.3-24.5-4.3-24.5 -12.4 5-22.1 6.4-34.2 4.8l2.6 19.1c6.1 0.1 11.9-0.2 10.6 4.1 -1.1 3.5-8.9 1.9-13.2 1.5 -1.1-0.1-2.5-1.3-2.7-2.4 -0.8-4.1-5.9-24.1-5.9-24.1 -11.6-5.4-20.5-12.5-26.8-24.6 0 0-15.9-2.5-23.1-6.6 -8.5-4.9-12.2-11.2-12.2-11.2 -8.5 0.3-10.3-7.6-6.2-11.3 4.7-4.3 13.9 0.1 9.6 8.5 0 0 4.6 5 12.7 7.8 9.8 3.4 18.7 3.4 18.7 3.4 1.8-11.8 6.7-30.2 12.5-43.2 0 0-9.3 0.1-15-0.4C29.6 80.2 28.8 74.4 28.9 72.5z"/>
  <path class="eye" d="M98.8 55.4c-0.4 4.3-4.2 7.1-8.2 7.8 -4.7 0.8-7.6-3.3-7.6-7.7s3.5-8 7.9-8.2C95.3 47 99.3 50.2 98.8 55.4z"/>
  <path class="iris" d="M96 55.3c-0.2 2.5-2.4 4.1-4.7 4.5 -2.7 0.5-4.4-1.9-4.4-4.4 0-2.5 2-4.6 4.6-4.7C93.9 50.5 96.2 52.4 96 55.3z"/>
  <path class="eye" d="M137.9 53.9c0 5-3.5 6.7-7.8 6.1 -4.6-0.7-8.2-2.6-8.2-7.3s2.9-8.2 8.4-8.2C135.2 44.5 137.9 48.7 137.9 53.9z"/>
  <path class="iris" d="M134.5 53.5c0 3-2.1 4-4.7 3.7 -2.8-0.4-4.9-1.6-4.9-4.4 0-2.8 1.7-4.9 5-4.9C132.8 47.9 134.5 50.4 134.5 53.5z"/>
  <path class="mouth" d="M104.5 65.8c-3.3 3.7-2 10.5 2.4 11.5 5.3 1.2 10.4-4.5 10.4-4.5s3.5 4.1 8.2 3.2c3.6-0.7 5.5-3.7 4.7-7.2 0 0-1.2 6.7-7.2 5.5 -3.9-0.8-4.3-5.2-4.6-6.9 0-0.2-0.3-0.3-0.5-0.1 -1.2 2-4.4 8.2-9.3 7.1C102.3 72.9 104.5 65.8 104.5 65.8z"/>
  <path class="tie" d="M109.8 101.4c0.8 1 5.7-1.3 7.3-2.2 0.3-0.2 0.7-0.1 0.9 0.1 1.2 0.9 4.3 3.2 5.9 2.7 2-0.6 4.7-9.2 2.6-9 -1.5 0.2-5.8 2.1-7.4 2.8 -0.4 0.2-0.9 0.2-1.3 0 -2.1-0.9-9-3.5-9.4-2C107.9 96 109.2 100.5 109.8 101.4z"/>
  <path class="crown" d="M65.8 14.3c-0.6-1.4 0.7-3 2.2-2.6l13.9 3.7c0.9 0.3 2 0.1 2.6-0.7l9.7-9.8c1-1.2 2.6-1.6 4-1.1l11.8 5.7c0.7 0.2 1.5 0.1 2.1-0.3l14.3-9c1-0.5 2.1 0.3 1.9 1.3l-3.9 25.9c-12.7 8.4-32 11.8-48.7 10.6L65.8 14.3z"/>
  <circle class="jewelry purple" cx="77.5" cy="21.8" r="2.4"/>
  <circle class="jewelry pink" cx="98.4" cy="17.1" r="2.4"/>
  <circle class="jewelry blue" cx="119.6" cy="14" r="2.4"/>
</svg>

[雑感]
トライキャッチ プリキュア

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.