流行っていなくとも、良いものは良い。美しいものは美しい。
『Nimを触り始め(る|た)けどドキュメント少なすぎ! なにから学べばいいかわからない!』
という人(過去の私)へ贈ります。
「このあたり知っておけば、言語理解が速いはず 」 って想いで書いています。
触り始めて間もない私なので粗があると思いますが、みなさんにとってNimへの良い入り口となれば幸いです
ちなみに私はWeb系に偏っており、ポインタ?美味しいよねぇ といった具合です。
詳しい記事への導線を配置しているので、この記事はあくまでも最初のつまづき用の杖だと思ってください。
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のバージョン管理 choosenim
brew install nim
公式サイトからのインストール 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】個人的逆引きリファレンス
最高!ほんと助かります
どんな関数があるの?ってときはソースコードを読むのがオススメです。
私は日本語ドキュメント依存のゆるふわエンジニア生活を送っていたために少々つらかったですが、関数内部の処理まで覗けるのは良いですね
私が最初のうちによく読んだのは lib/system.nim です。
あとは公式ドキュメントの Index を⌘ + F
するとか
キャメル? スネーク?
キャメルケース、スネークケースどちらでも書けます。たとえ宣言済みの変数であっても
でも、公式ではキャメルケースが推奨されています。じゃあスネーク禁止にしたほうがよくない!?
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 repr(holyGrail)
デバッグ向きechoとしてdebugEcho
がありますが、
プロシージャ(関数)の副作用...?
コメント読んでも何を言ってるのかわかりませんでした
(デバッグ時にのみechoするのかな)
ステップ実行はこちらの記事を読んでいただけると。
nimのデバッグ
(私は試してないです)
変数/定数の宣言
宣言は三種類あります。
const
定数です。コンパイル時に値がわかるものでないといけません。
例えば、コンソールから入力されるような値は代入できません。この場合はlet
を使います。
var
他言語でもよく見る、普通の変数です。
let
再代入が禁止されたvar
です。コンパイル時に値が決まっている必要はありません。
この項目は他の方もまとめられているのですが、本記事を読み進められるよう、一応書きました!
@2vgさん、アドバイスありがとうございました
Stringへの型変換
型変換は様々ですが、Stringにできそうな大抵の型は$
がサポートされていて、手前に付けると文字列になります。
var number = 42
echo "答えは" & $number & "です。"
&
は文字列連結です。
echo
関数では内部的に$
が使われる上に、複数の引数を受け付けるので、以下のようにも書けます。
var number = 42
echo "答えは", number, "です。"
関数(proc, プロシージャ)への引数渡し
第一引数を関数の手前に書いてメソッドチェーン風にしたり、カッコを省略できたりします。これ好き
write
関数を例に挙げます。
write(stdout, str)
stdout.write(str)
stdout.write str
write stdout, str
stdout.write"これもOK"
ネスト
let
やvar
等はネストでまとめることができます。
const wrong = "ポピプテピック"
const correct = "ポプテピピック"
var message: string
const
wrong = "ポピプテピック"
correct = "ボブネミミッミ"
var
message: string
また、Nimでは主に2スペースのインデントが使われます。NimとPythonを交互に書くと酔いますね
破壊的な関数
Nimに限った話では無いですが、再代入が禁止されている const
let
に対して破壊的な(変数の内容を書き換える)関数を使うとエラーになります。
string型にstring型を追加するadd
関数の例です。
proc add(x: var string, y: string)
var cuteVoice = "piyo"
cuteVoice.add("piyopiyo")
let cuteVoice = "piyo"
cuteVoice.add("gieeeeeeeeeeee!!!")
Nimの本
コメントにて、Nimの本を教えていただきました。
公式サイトに載ってたのにガンスルーしてました...
LiveBook形式ならブラウザで開くことで、Google翻訳できるみたいです!
@6inさん、ありがとうございます
頂いた質問への回答
FizzBuzzのイミュータブルな解決法は?
現バージョンでforEachの実装が見当たらなかったため、このような書き方になるかと思います。
配列に足していく書き方をすると、パフォーマンスが結構落ちるかも。
Nimmerの先輩方!もっといい方法あれば、教えていただけると
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
改善が望まれます
(これで洗練されている説もあります)
string interpolation はある?
string interpolation = 文字列補間
strutils というライブラリで%
を使って実現できます。
import strutils
echo "答えは $answer です。" % ["answer", $42]
# => 答えは 42 です。
文字列内での変数展開のような実装は見つけることができませんでした。
これもあるといいなぁって思います。
Nim 0.18.0 でstrformatが追加されました
Nim 0.18.0のリリースまとめ #strformatモジュール
Module strformat
import strformat
const name = "りがに"
echo "今日のごはんは?"
echo fmt"わぁい {name}{readLine(stdin)}すこ"
おまけ
原型を保ったまま速度を詰めたFizzBuzz(100万回転)を置いておきます
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)は私の落書きなので、非公式です。流行って!
「最初の頃、このへんでハマったよ」みたいなものがあれば、教えてください
また、Nimはdevelブランチが活発で、将来この記事に書いてある幾らかは嘘っぱちになることも有り得ますので、お気を付けください
(嘘っぱちになったら ご指摘いただけると最高な気持ちです)
追記: NimroをSVG化したので置いておきます(Rejectされました)
<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>
[雑感]
トライキャッチ プリキュア