Nim基礎文法最速マスター

# Nimのダウンロード
[公式HP](https://nim-lang.org/)からどうぞ。
ドキュメントやフォーラムもここらへんにあります。

[choosenim](https://github.com/dom96/choosenim)を使うと、Nimのバージョン管理が楽になりおすすめです。（[解説記事](https://qiita.com/honeytrap15/items/2c7ebe20cb69df8c53c9)）

インストールしている間、[オンライン実行環境](https://play.nim-lang.org/#ix=2lK1)でお試しください。

# バージョン
1.4.0での動作を確認しています。

# 基礎
## 言語の概観
Nimは "Efficient, Expressive, Elegant" を標榜するプログラミング言語です。

### Effcient: 効率的
NimはC言語並みに処理速度が速いと巷に評判のコンパイル言語です。（なお、知名度がない）
また、構文がシンプルで親切なので、プログラミング自体が効率的です。

### Expressive: 表現豊か
Nimは非常に自由度の高い言語です。
一例として、演算子（+とか-みたいなやつ）が自由に作れます。

```nim
import math
proc `$` (x, y: int): int = # 組み合わせ
  return fac(x) div (fac(y) * fac(x - y))

echo 5 $ 2 # => 10
```

そのほか、関数型にもオブジェクト指向にも対応できる柔軟性を持ちます。

### Elegant: 優雅
インデントを使うため、pythonと同じような見た目になります。賛否両論はあると思いますが、すっきりした見た目をしています。
メタプログラミングにも長けており、高いレベルの問題解決ができます。

## ファイルのコンパイル・実行

```console
nim c -r [ファイル名]
```

で実行してくれます。拡張子は`.nim`がいいでしょう。
（[コマンドラインのオプションの詳細](https://nim-lang.org/docs/nimc.html)）

これを実行すると、C言語（C++やjavascriptも可）を介してコンパイルし、実行されます。

## サンプルコード
fizzbuzzのサンプルコードです。
今はわからなくても、この記事を読めば理解できるようになるでしょう。

```nim
from strutils import parseInt

echo "Input max Number: "
let maxNumber: int = readLine(stdin).parseInt

for i in 1..maxNumber:
  if i mod 15 == 0: # 15の倍数
    echo "fizzbuzz"
  elif i mod 3 == 0: # 3の倍数
    echo "fizz"
  elif i mod 5 == 0: # 5の倍数
    echo "buzz"
  else:
    echo $i
```

## 標準入力・標準出力
標準入力は`readLine(stdin)`で、標準出力は`echo hoge`です。
以下、サンプルです。

```nim
echo "Your name? : "
var name: string = readLine(stdin)
echo "Hello　", name, "!"
```

## コードブロック
pythonと同じく、インデントでブロックを表現しますが、スペース2個を使ってください。
TABを使うとエラーになります。

## 関数への引数渡し
実行する際の関数の書き方には、さまざまな方法があります。
文字列に文字を追加する関数`add`で例を示します。

```nim
var welcome = "Welcome to"
add(welcome, ' ')
add welcome, 'N'
welcome.add 'i'
welcome.add('m')
welcome.add'!'
echo welcome

# => Welcome to Nim!
```

hogeの型がstring以外の場合、`$`を頭につけてください。この`$`は単に「文字列以外を文字列に変換する関数」で、他言語(Perlのsigil, Haskelのカッコなど)のような意味は持ちません。

## コメント
`#`を書くと、行の最後までコメントになります。
複数行のコメントには、`#[ ]#`が使えます。

```nim
echo "Hello, World" #ハローワールド
#[
echo "お名前は？ "
var name: string = readLine(stdin)
echo "やあ、", name, "!"
]#
```

## 宣言と代入

```nim
var x : int     # 変数の宣言だけ
x = 0           # 代入だけ
var y : int = 1 # 宣言と代入
var z       = 2 # 型が明らかなので省略
let w : int = 3 # 定数

# 同時に宣言や代入
var x, y = 3 # どちらもintの3になる
let
  a = 1
  b = 2
  c = 3
```

定数には`const`もありますが`let`との大きな違いとして、`const`はコンパイル時に値が決まっている必要があります。例えば「標準入力を定数に格納したい」といった操作は、標準入力がコンパイル時にわからないので`let`のみに格納できます。


```nim
# どちらもOK
let int_var = 3
const int_var = 3

# letのみ
let str_var = readLine(stdin)
const str_var = readLine(stdin) # コンパイルエラー
```

## 型
`type(x)`としてください。

```nim
var x = 'a'
echo type(x) # => char
```

### int/float/bool型

```nim
# 宣言
var
  int_var : int # 整数型
  flt_var : flaot # 浮動小数点型
  bol_var : bool # ブール型

# 各種演算
## int同士の加減乗除
int_var = 1 + 1 - 2 * 3 # => -4
int_var += 1    # => -3
int_var = 21 div 13 # => 1 （整数の商）
int_var = 21 mod 13 # => 8 （余り）

## float同士の加減乗除
flt_var = 2.0 + 3.0 - 5.0 * 4.0 # => -15.0
flt_var = 5.0 / 2.0 # => 2.5 （intとの相違点）

## int同士の比較(floatも同様)
bol_var = 2 <  3 # => true  # >, <=, >= も使える
bol_var = 2 == 4 # => false
bol_var = 2 != 3 # => true

## intとfloat同士も演算可能
flt_var = 2.0 - 3 * 5.2 # => -13.6

## bool同士の演算
bol_var = not true # => false
bol_var = true and false # => false # or, xorも使える
bol_var = (3 > 0) == true # => true
bol_var = false < true # => true

## 型変換
int_var = int(32.2 / 3) # => 10
int_var = toInt(32.2 / 3) # => 10
flt_var = float(32 div 3) # => 10
flt_var = toFloat(32 div  3) # => 10
```

ほとんど予想通りかと思いますが、以下の点に注意してください。

- floatの割り算は`/`ですが、intでの割り算は`div`（商）, `mod`（余り）です。
- 累乗は`math`ライブラリにある、`pow`あるいは`^`（指数が自然数のときのみ）を使います。

また、int, floatの型にはいくつか種類があります。（[詳細](https://nim-lang.org/docs/tut1.html#basic-types)）

### char/string型

```nim
# 宣言
var
  chr_var : char # 文字型
  str_var : string # 文字列型
  
  int_var : int
  bol_var : bool

chr_var = 'V' # リテラルは単引用符
str_var = "Variable" # リテラルは二重引用符
str_var = """long literal that can use " " too
and multiple lines"""
    # `"""`で挟むとエスケープ不要で改行可能な文字リテラルが作れる。

# ASCII文字コードとの変換
var int_var = ord('A') # => 65
chr_var = chr(65) # => 'A'

# 文字列操作
str_var = "aa" & "bb" # => "aabb" # 連結
str_var = $1.0 # => "1.0" # 文字列へ変換
bol_var = 'a' in "abc" # => true # 文字が文字列に含まれる
bol_var = "ab" in "abc" # => true # 文字列が文字列に含まれる
int_var = len("abc") # => 3 # 文字数
```

より詳しい文字列操作は[標準ライブラリ](# 標準ライブラリ)を参照してください。

### 配列（コンテナ型）
配列のような型（コンテナ型）のうち重要なものは以下の5つあります。

- array型（固定長配列）
- seq型（可変長配列）
- set型（集合型）
- tuple型（タプル型）
- Table型（連想配列）

また、関数の引数として、

- openArray型
- varargs型

を使うことができます。

[set型](https://nim-lang.org/docs/manual.html#types-set-type)、[tuple型](https://nim-lang.org/docs/manual.html#types-tuples-and-object-types)、[openArray型](https://nim-lang.org/docs/manual.html#types-open-arrays)、[varargs型](https://nim-lang.org/docs/manual.html#types-varargs)に関しては、マニュアルを参照してください。
Table型に関しては、[標準ライブラリ](# 標準ライブラリ)を参照してください。

#### array型（固定長配列） / seq型（可変長配列）

```nim
# 宣言
var
  int_arr: array[4, int] =  [0, 3, 5, 2] # リテラルは`[]`
  int_seq: seq[int]      = @[5, 2, 8, 1] # リテラルは`@[]`

  int_var : int
  bol_var : bool

# for文で順番にアクセスできる 
for value in int_arr: # int_seqも同様
  echo value
# => 0
# => 3
# => 5
# => 2

# 基本的な配列操作
## array, seqに共通してできること
int_var = len(int_arr) # => 4 # 配列長
int_var = int_arr[2] # => 5 # 要素にアクセス
var int_subarr = int_arr[1..3] # => [3, 5, 2] # 部分列の取り出し
int_arr[0] = 4 # => int_arr <- [4, 3, 5, 2] # 要素を書き換え
int_var = max(int_arr) # => 5 # 最大値（最小値はmin）
bol_var = int_arr == [4, 3, 5, 2] # => true # 比較

## arrayからseqに変換
int_seq = @int_arr # => @[4, 3, 5, 2]

## arrayにできるが、seqにできないこと
   # 特にないかな？

## seqにできるが、arrayにできないこと
int_seq = @[5, 2] & @[8, 1] # => @[5, 2, 8, 1] # 要素の連結
add(int_seq, 4)  # int_seq    <- @[5, 2, 8, 1, 4] # 要素の追加
add(int_seq, [1, 3]) # 配列の追加
                 # int_seq    <- @[5, 2, 8, 1, 4, 1, 3]
del(int_seq, 1)  # int_seq    <- @[5,    8, 1, 4, 1, 3] # 要素の削除
```

上記コードのように、`@`は固定長配列(array)の頭につけることで動的配列(seq)を意味します。
より詳しい配列操作は[標準ライブラリ](# 標準ライブラリ)を参照してください。

## 制御文
制御文には以下のものがあります。

- if-elif-else
- case-of-else
- while
- for
- try-except

前述した通り、ブロックはインデント（スペース2個）で表現します。

```nim
var int_var = 1

# if文
if (int_var == 1):
  echo "value is 1"
elif (int_var > 1):
  echo "value is greater than 1"
else:
  echo "value is smaller than 1"

# case文
case (int_var)
of 1:
  echo "value is 1"
of 2..100:
  echo "value is greater than 1"
of -100..0:
  echo "value is smaller than 1"
else:
  echo "value is extreme"

# while文
while true:
  int_var += 1
  if int_var == 3:
    continue
  if int_var > 5:
    break
  echo $int_var
# => 1
# => 2
# => 4
# => 5

# for文 # continue, breakも使える
for i in 2..5: # 2..<6とも書ける
  echo $i
# => 2
# => 3
# => 4
# => 5

# try文
echo "Please input:"
var str_var = readLine(stdin)
try:
  echo str_var[100000]
except IndexDefect:
  echo "line too short"
finally: 
  raise newException(ValueError, "you are doomed")
```

for文で配列のインデックスを使う場合

```nim
for i in 0..<len(arr):
```

ではなく

```nim
for i in low(arr)..high(arr):
for i, v in arr: # iにインデックス、vに値が入る
```

を使った方がいいでしょう。[^1]
[^1]: array型の場合、インデックスが0から始まると限りません。（[マニュアル](https://nim-lang.org/docs/tut1.html#advanced-types-arrays)）

## 関数

関数[^2]を定義するときは`proc`を使います。

```nim
proc mean0(x: int, y: int, z = 2.0): float =
  return float(x + y) / z

proc mean1(x, y: int, z: float = 2.0): float =
  result = float(x + y) / z

proc no_return(x, y: int) =
  echo $x & $y

echo mean0(1, 2) # => 1.5
echo mean0(1, 5, 3.0) # => 2.0
echo mean0(z = 1.0, x = 3, y = 2) # => 5.0
echo 3.mean0(3) # => 3.0 # わからなかったら「関数への引数渡し」を読んでね！
no_return(1, 2) # => 12

int_var = (proc(x, y: int): int = x + y)(1, 2) # => 3 # 無名関数
```

返り値は、`return`文を使うほか、関数内であらかじめ定義されている`result`変数に代入する、一行で書けるなら`=`につなげる、などで表現できます。

[^2]: Nimでは副作用を有しうる`proc`と副作用のない`func`の区別があるが、副作用のない`proc`も作れるので本稿では区別しない。

### オーバーロード
Nimの関数はオーバーロードができます。
これはつまり、名前が同じ関数であっても引数の型が違えば、別の関数として定義できます。

```nim
proc twice(x: int): int = x * 2 # 一行ならこんな書き方もあるよ
# proc twice(x: int): int = ... # 既に同じ名前の関数があるのでエラー
proc twice(x: float): float = x * 2 # 型が違うならOK
```

### ジェネリクス
オーバーロードとは逆に、いくつかの型に共通した処理をする関数を作りたい場合、ジェネリクスが使えます。ジェネリクスは関数の横の`[]`で表現します。
例えば、上記の`twice(int)`と`twice(float)`は下の関数でまとめられます。

```nim
proc twice[T](x: T): T = x * 2
```

# 型の定義
ユーザーが新しく型を定義することができます。オブジェクト指向プログラミングと相性がいいでしょう。

```nim
type Character = object of RootObj # 継承されるには`of RootObj`が必要
  name: string
  hp: int

proc echoName(self: Character) = # メソッドは型を
  echo self.name

type Player = object of Character # 継承
  atk: int

let Hero = Player(name: "あああああ", hp: 10, atk: 2)
Hero.echoName # => "あああああ"
```

`type`文はそのほか、特定の配列を定義したり、列挙型を定義するのにつかわれます。

```nim
type
  Direction = enum
    north, east, south, west

var dir_var: Direction = south

type
  IntArray = array[5, int] # 長さ5, 型がintの固定長配列
var x: IntArray
x = [1, 2, 3, 4, 5, 6]

```

# ファイル入出力

```nim
# ファイルを読む
var lines: seq[string] = @[]
block:
  var f : File = open("read_from.txt" , FileMode.fmRead)
  defer :
    close(f)
  while not f.endOfFile:
    lines.add(f.readLine())

# ファイルに書く
block:
  var f : File = open("write_to.txt" , FileMode.fmWrite)
  defer:
    close(f)
  for line in lines:
    f.writeLine(line)
```

block, deferに関しては、マニュアルを参照してください。

# モジュール・ライブラリ
ほかのモジュールをインポートするには`import`文を使います。
ほかのモジュールへエクスポートしたい変数、関数には`*`をつけて置きましょう。

```nim:module_a.nim
var
  export_this* = 1
  dont_export = 2
```

```nim:module_b.nim
import module_a

echo export_this
echo dont_export # これはエラー
```

`from module import hoge`を使うと、欲しいものだけをインポートできます。
# 標準ライブラリ
Nimは"battery included"で、[標準ライブラリ](https://nim-lang.org/docs/lib.html)が充実しています。外部ライブラリは[Nimble](https://github.com/nim-lang/nimble)が使われます。choosenimを使ったら一緒に入っています。
ここでは、

- 数学: [math](## math), [random](## random)
- 文字列操作: [strutils](## strutils), [strformat](## strformat), [unicode](## unicode)
- 配列操作: [algorithm](## algorithm), [sequtils](## sequtils), [tables](## tables), [json (& marshal)](## json関連)

について扱います。

以下で使う変数を宣言しておきます。

```nim
var
  chr_var : char
  str_var : string
  int_var : int
  flt_var : float
  bol_var : bool
  str_seq : seq[string]
  int_seq : seq[int]
```

## math

```nim
import math

## 定数
echo PI # => 3.141592653589793
echo E  # => 2.718281828459045

## 関数
int_var = sum([1, 2, 3]) # => 6  # 合計
int_var = fac(4)         # => 24 # 階乗
int_var = 2.pow(5)       # => 32 # 累乗
    # そのほか、sin, log, roundなどがあります
```

## random

```nim
import random

randomize() # seedのランダム化。やらないと以下の結果が同じになる。

int_var = rand(100) # => 0-100までの乱数
str_var = ["Ace", "King", "Queen", "Jack"]
shuffle(str_var) # str_varがランダムな順番になる
```


## strutils

```nim
import strutils

# 文字列操作
str_seq = "ab,bc,cd".split(",") # => @["ab", "bc", "cd"] # 分割
str_var = "hello".repeat(3) # => "hellohello" # 繰り返し
bol_var = startsWith("Hello", "He") # => true # 開始文字
int_var = "Hello".count('l') # => 2 # カウント
str_var = "Hello".replace("lo", 'p') # => Help # 置き換え

# 型変換
int_var = "1".parseInt # => 1
flt_var = "2.0".parseFloat # => 1.0
    # 文字列への変換は前述した`$`

# 文字の種類の判定
bol_var = isAlphaAscii('e') # => true # アルファベットか判定
bol_var = isDigit('8') # => true # 0-9か判定
    # そのほか、大文字小文字やの判定・変換があります。
```

## strformat

```nim
import strformat

flt_var = 2.0

str_var = &"---{flt_var + 1}---" # => "---3.0---" # 変数や計算
str_var = echo &"{flt_var = }" # => "flt_var = 2.0" # 変数の値の表示
str_var = &"{-12:05}" == " -0012" # 0埋め
str_var = &"{12.34:@>8.5f}" # => "@@12.340" # 小数点以下桁数、右寄せ、文字埋め
str_var = &"{12.0:!^16.3e}" # => "!!!1.200e+01!!!!" # 指数表記
```

## unicode

```nim
import unicode

str_var = reversed("123456abc") # => "cba654321"
str_var = "日本語も使えるよ"
int_var = len(str_var) # => 24
    # 非ASCII文字の長さは`len(string)`で取得できない
int_var = str_var.runeLen # => 8
str_seq = str_var.toRunes # => @["日", "本", "語", "も", "使", "え", "る", "よ"]
    # 配列操作でできることができる(長さを取得したり、置換したり)
```

## algorithm

```nim
import algorithm

str_seq = @["ab", "aa", "bc", "ba"]
str_seq.sort()    # str_seq <- @["aa", "ab", "ba", "bc"]
str_seq.reverse() # str_seq <- @["bc", "ba", "ab", "aa"]
int_var = binarySearch(str_seq, "ba") # => 1
```

## sequtils

```nim
import sequtils

int_var = "abracadabra".count('a') # => 5
int_seq = repeat(5, 3) # => @[5, 5, 5]
int_seq = cycle(@[1, 2], 3) # => @[1, 2, 1, 2, 1, 2]
int_seq = @[1, 2, 3].map(proc(x: int): float = x.toFloat) 
    # => @[1.0, 2.0, 3.0]
```

## tables

Nimにはtablesをインポートしなくても`{key0: value0, key1: value1}`という記法は存在しますが、これはあくまでも`[(key0, value0), (key1, value1)]`と同等のシンタックスシュガーです。（`()`はタプル）
`hoge[key0]`という形で`value0`にアクセスするには、`Table`に変換する必要があります。

```nim
import tables

var int_str_table = {1: "one", 2: "two"}.toTable # 変換
int_str_table[3] = "three" # 代入
str_var = int_str_table[2] # => "two"
```

## json

jsonを扱う場合、`JsonNode`型を使います。これは文字列としてのjsonとNimのオブジェクトをつなぐ役割をします。

以下のコードでも説明しますが、模式図を載せます。

```
+--------+   %*   +----------+       $       +--------+
|        | -----> |          | ------------> |        |
| Object |        | JsonNode |               |  json  |
|        | <----- |          | <------------ |        |
+--------+   to   +----------+   parseJson   +--------+
```

```nim
import json

type
  User = object # 継承しないので`of RootObj`は不要
    name, surname: string
    age: int 
    

var user_seq: seq[User] = @[
User(name: "Hatiman", surname: "Hikigaya", age: 17),
User(name: "Yukino", surname: "Yukinoshita", age: 16),
User(name: "Yui", surname: "Yuigahama", age: 17),
]
# オブジェクト -> JsonNode
var user_json_node: JsonNode = %* user_seq


echo user_json_node[0]["name"].getStr()
# => Hatiman

user_json_node[0]["name"] = %* "Hachiman"
# もっと複雑な型（ネストが深いなど）も代入可能

echo user_json_node[0]["name"].getStr()
# => Hachiman


# JsonNode -> json（の文字列）
str_var = $user_json_node
    # => """[{"name":"Hachiman","surname":"Hikigaya","age":17},{"name":"Yukino","surname":"Yukinoshita","age":16},{"name":"Yui","surname":"Yuigahama","age":17}]"""
    # $を使ってJsonNodeの文字列表現を取り出す。

str_var = """{"name":"Iroha","surname":"Issiki","age":16}"""

# json（の文字列）-> JsonNode
user_json_node = parseJson str_var

# JsonNode -> オブジェクト
var new_user : User = user_json_node.to(User)

user_seq.add(new_user)
    # user_seq <-
    # @[
    #   User(name: "Hachiman", surname: "Hikigaya",    age: 17),
    #   User(name: "Yukino",   surname: "Yukinoshita", age: 16),
    #   User(name: "Yui",      surname: "Yuigahama",   age: 17),
    #   User(name: "Iroha",    surname: "Issiki",      age: 16),
    # ]
```

# そのほかの機能
ここで紹介した基礎的な機能のほか、Nimには

- ポインタ
- Pragma
- メタプログラミング(Template, Macro)
- javascriptへの変換
- 多言語との連携

といった多くの機能があります。ご興味があればぜひ試してみてください。