🧬

【Nim Lang】配列処理チートシート

2022/08/07に公開

この記事は?

Nim Langの組み込み型の配列についてまとめた記事です。
※記事中の例の動作確認にはNim ver1.06を用いています。

配列に関する処理一覧

Procedure

ライブラリ procedure名 破壊 非破壊
system []
system add
system contains
system high
system len
system max
system min
algorithm binarySearch
algorithm fill
algorithm isSorted
algorithm lowerBound
algorithm merge
algorithm nextPermutation
algorithm prevPermutation
algorithm product
algorithm reverse
algorithm reversed
algorithm rotatedLeft
algorithm rotateLeft
algorithm sort
algorithm sorted
algorithm upperBound
sequtils all
sequtils any
sequtils apply
sequtils concat
sequtils count
sequtils cycle
sequtils deduplicate
sequtils distribute
sequtils filter
sequtils insert
sequtils keepIf
sequtils map
sequtils repeat
sequtils zip

Templates

ライブラリ procedure名 破壊 非破壊
sequtils newSeqWith
sequtils toSeq

[] (slice)

ライブラリ procedure名 破壊 非破壊
system []
let seq = @[0, 1, 2, 3, 4]

# []の中にsliceを入れることで、対応する部分配列を`seq`として得ることができます
let sliced_1 = seq[0..2] # @[0, 1, 2]

# nimのsliceは右端の値を含みます(閉区間と言ってもいい)
# a..<b のような形で書けば右端の値を含めないことができます(こちらは半開区間的と言える)
let sliced_2 = seq[0..<3] # @[0, 1, 2]

# arrayに対して使用しても、部分配列はseqとして返ってきます
let array = [0, 1, 2, 3, 4]
let sliced_3 = array[0..2] # @[0, 1, 2]

add

ライブラリ procedure名 破壊 非破壊
system add
# addは配列長が固定でないseqに対してしか使用できません
var seq = @[0, 1, 2, 3, 4]

seq.add(5) # @[0, 1, 2, 3, 4, 5]

# letで宣言したseqに破壊的なメソッドを使用しようとするとコンパイルエラーになります。
let seq = @[0, 1, 2, 3, 4]

seq.add(5) # ERROR!

contains

ライブラリ procedure名 破壊 非破壊
system contains
# 配列にある要素が含まれているならtrueを、そうでないならfalseを返します
let arr = [0, 1, 2, 3, 4]

let item_1 = 2
let item_2 = 5

let has_item_1 = arr.contains(item_1) # true
let has_item_2 = arr.contains(item_2) # false

省略形として、in演算子とnotin演算子を使用できます。

let arr = [0, 1, 2, 3, 4]

let item_1 = 2
let item_2 = 5

let has_item_1 = item_1 in arr # true
let has_not_item_2 = item_2 notin arr # true

high

ライブラリ procedure名 破壊 非破壊
system high

関連:low

配列の一番大きいインデックスを返します。

let s = @[1, 2, 3]
echo s.high # 2

※Nimでは、Arrayを宣言する時の第一型引数にrangeを渡し、インデックスの一番小さい値と大きい値を指定できます。

var arr_1: array[1..5, int] # 1スタートのインデックス
echo arr_1.high # 5

var arr_2: array[-5..(-2), int] # マイナスのindexも使用できる。構文解析の優先順位の関係か、終端に()が必要
echo arr_2.high # -2

len

ライブラリ procedure名 破壊 非破壊
system len

配列の要素数を返します。

let s = @[1, 2, 3]
echo s.len # 3

max

ライブラリ procedure名 破壊 非破壊
system max

// TODO: 比較の仕組みについて(いろいろな型で)
配列の最大値を返します。比較には.<プロシージャが呼ばれます。

# int型の場合
let int_seq = @[1, -1, 5]
echo int_seq.max # 5

# string型の場合
let str_seq = @["anomalocaris", "bear", "cat" , "dog"]
echo str_seq.max # dog

min

ライブラリ procedure名 破壊 非破壊
system min

配列の最小値を返します。

# int型の場合
let s = @[1, -1, 5]
echo s.min # -1

binarySearch

ライブラリ procedure名 破壊 非破壊
algorithm binarySearch
var s = @[10, 24, 2, 12, 18, 34]
# sは事前にソートをする必要があります。
s.sort() # -- @[2, 10, 12, 18, 24, 34]
echo s.binarySearch(10)  # 1 -- idxの1が返ってくる
echo s.binarySearch(11)  # -1 -- 見つからなければ-1を返す

fill

ライブラリ procedure名 破壊 非破壊
algorithm fill
var seq = @[0, 0, 0, 0, 0]
seq.fill(1, 3, -1) # (第一引数..第二引数)のレンジに対応する箇所を第三引数で埋める
echo seq # @[0, -1, -1, -1, 0]

# 使用例:Arrayの初期化
var arr: array[5, int]
arr.fill(0, 4, 0)
echo arr # [0, 0, 0, 0, 0]

isSorted

ライブラリ procedure名 破壊 非破壊
algorithm isSorted

計算量はO(n)となる。

# 配列がsortされていたらtrueを、されていなかったらfalseを返す
var seq_1 = @[0, 1, 2, 3, 4]
var seq_2 = @[4, 3, 2, 1, 0]
var seq_3 = @[0, 3, 2, 4, 1]

echo seq_1.isSorted() # true
echo seq_1.isSorted(Ascending) # true
echo seq_2.isSorted() # false
echo seq_2.isSorted(Descending) # true
echo seq_3.isSorted() # false

lowerBound

ライブラリ procedure名 破壊 非破壊
algorithm lowerBound
# 配列内の`X以上`を満たす最初の値のidxを返します。
let seq = @[0, 1, 2, 3, 4, 5, 10, 11, 12, 13]
echo seq.lowerbound(10) # 6
echo seq[seq.lowerbound(10)] # 10

merge

ライブラリ procedure名 破壊 非破壊
algorithm merge
var arr_1 = @[10]
var arr_2 = @[2, 4, 6] # sortされている必要アリ
var arr_3 = @[1, 2, 3]  # sortされている必要アリ

# arr_2とarr_3をソートしたものが、arr_1に追加されます。
arr_1.merge(arr_2, arr_3)

echo arr_1 # @[10, 1, 2, 3, 4, 5, 6]

nextPermutation

ライブラリ procedure名 破壊 非破壊
algorithm nextPermutation

配列を順列とみなした時の、次の順列を返します。
例えば、@[1, 3, 2, 4, 5]という配列があったときは、それは順列

@[1, 2, 3, 4, 5]
@[1, 2, 3, 5, 4]
@[1, 2, 4, 3, 5]
@[1, 2, 4, 5, 3]
@[1, 2, 5, 3, 4]
@[1, 2, 5, 4, 3]
...

の一部としてみなされ、順列内の次の値に相当する@[1, 3, 4, 2, 5]で与えられた配列を更新します。

var arr_1 = @[1, 3, 2, 4, 5]
echo nextPermutation(arr_1) # true
echo arr_1 # @[1, 3, 4, 2, 5]

# 順列の最後の値を渡された場合、falseを返します。
var arr_2 = @[5, 4, 3, 2, 1]
echo nextPermutation(arr_2) # false
echo arr_2 # @[5, 4, 3, 2, 1]

prevPermutation

ライブラリ procedure名 破壊 非破壊
algorithm prevPermutation

文字通り、nextPermutationの逆を行います。与えられた配列をある順列の要素とみなした時の、一個前の順列要素を返します。

var arr_1 = @[1, 3, 2, 4, 5]
echo prevPermutation(arr_1) # true
echo arr_1 # @[1, 2, 5, 4, 3]

# 順列の最初の値を渡された場合、falseを返します。
var arr_2 = @[1, 2, 3, 4, 5]
echo prevPermutation(arr_2) # false
echo arr_2 # @[1, 2, 3, 4, 5]

product

ライブラリ procedure名 破壊 非破壊
algorithm product

[seq-A, seq-B, seq-C, ...]という値に対して、seq-A × seq-B × seq-Cといった直積を返します。
注意:あまりにも直積の次元が大きいと、計算量が爆発します。

let seq_1 = @[1, 2, 3]
let seq_2 = @[1, 2, 3]

echo product(@[seq_1, seq_2]) # @[@[3, 3], @[2, 3], @[1, 3], @[3, 2], @[2, 2], @[1, 2], @[3, 1], @[2, 1], @[1, 1]]

reverse

ライブラリ procedure名 破壊 非破壊
algorithm reverse

配列の順番を逆転させます。

var seq_1 = @[1, 2, 3, 4, 5]
seq_1.reverse
echo seq_1 # @[5, 4, 3, 2, 1]

reversed

ライブラリ procedure名 破壊 非破壊
algorithm reversed

配列の順番を逆転させます。reverseの非破壊版です。

let seq_1 = @[1, 2, 3, 4, 5]
let seq_2 = seq_1.reversed

echo seq_1 # @[1, 2, 3, 4, 5]
echo seq_2 # @[5, 4, 3, 2, 1]

rotatedLeft

ライブラリ procedure名 破壊 非破壊
algorithm rotatedLeft

配列を回転させます。(一番左にあるものを一番右に持ってくるなど)rotateLeftの非破壊版です。

let seq_1 = @[1, 2, 3, 4, 5]
let seq_2 = seq_1.rotatedLeft(1)
echo seq_2 # @[2, 3, 4, 5, 1]

# 負の値を入れることで逆回転ができます。
let seq_3 = seq_1.rotatedLeft(-1)
echo seq_3 # @[5, 1, 2, 3, 4]

# Sliceを指定することで、ローテーションの範囲を指定することができます。
let seq_4 = seq_1.rotatedLeft(1..3, 1)
echo seq_4 # @[1, 3, 4, 2, 5]

rotateLeft

ライブラリ procedure名 破壊 非破壊
algorithm rotateLeft

配列を回転させます。(一番左にあるものを一番右に持ってくるなど)rotateLeftの非破壊版です。

var seq_1 = @[1, 2, 3, 4, 5]
seq_1.rotateLeft(1)
echo seq_1 # @[2, 3, 4, 5, 1]

# 負の値を入れることで逆回転ができます。
seq_1.rotateLeft(-1)
echo seq_1 # @[5, 1, 2, 3, 4]

# Sliceを指定することで、ローテーションの範囲を指定することができます。
seq_1.rotateLeft(1..3, 1)
echo seq_1 # @[1, 3, 4, 2, 5]

sort

ライブラリ procedure名 破壊 非破壊
algorithm sort

配列を並び替えます。

var seq_1 = @[2, 3, 5, 1, 4]
seq_1.sort
echo seq_1 # @[1, 2, 3, 4, 5]

# 降順・昇順を明記できます。
seq_1.sort(Ascending)
echo seq_1 # @[1, 2, 3, 4, 5]
seq_1.sort(Descending)
echo seq_1 # @[5, 4, 3, 2, 1]

# 独自のcomparaterを渡すことができます。
var seq_2 = @[("Carol", 21), ("Ted", 22), ("Bob", 32), ("Alice", 18)]

proc cmpByName(x, y: (string, int)): int =
  cmp(x[0], y[0])
proc cmpByAge(x, y: (string, int)): int =
  cmp(x[1], y[1])

seq_2.sort(cmpByName)
echo seq_2 # @[("Alice", 18), ("Bob", 32), ("Carol", 21), ("Ted", 22),]
seq_2.sort(cmpByAge)
echo seq_2 # @[("Alice", 18), ("Carol", 21), ("Ted", 22), ("Bob", 32)]
seq_2.sort(cmpByAge, Descending)
echo seq_2 # @[("Bob", 32), ("Ted", 22), ("Carol", 21), ("Alice", 18)]

sorted

ライブラリ procedure名 破壊 非破壊
algorithm sorted
sortの非破壊版です。
let seq_1 = @[2, 3, 5, 1, 4]
let seq_2 = seq_1.sorted
echo seq_2 # @[1, 2, 3, 4, 5]

# 降順・昇順を明記できます。
let seq_3 = seq_1.sorted(Ascending)
echo seq_3 # @[1, 2, 3, 4, 5]
let seq_4 = seq_1.sorted(Descending)
echo seq_4 # @[5, 4, 3, 2, 1]

# 独自のcomparaterを渡すことができます。
let seq_5 = @[("Carol", 21), ("Ted", 22), ("Bob", 32), ("Alice", 18)]

proc cmpByName(x, y: (string, int)): int =
  cmp(x[0], y[0])
proc cmpByAge(x, y: (string, int)): int =
  cmp(x[1], y[1])

let seq_6 = seq_5.sorted(cmpByName)
echo seq_6 # @[("Alice", 18), ("Bob", 32), ("Carol", 21), ("Ted", 22),]
let seq_7 = seq_5.sorted(cmpByAge)
echo seq_7 # @[("Alice", 18), ("Carol", 21), ("Ted", 22), ("Bob", 32)]
let seq_8 = seq_5.sorted(cmpByAge, Descending)
echo seq_8 # @[("Bob", 32), ("Ted", 22), ("Carol", 21), ("Alice", 18)]

upperBound

ライブラリ procedure名 破壊 非破壊
algorithm upperBound
# 配列内の`X以上`を満たす最初の値のidxを返します。
let seq = @[0, 1, 2, 3, 4, 5, 10, 11, 12, 13]
echo seq.upperbound(10) # 7
echo seq[seq.upperbound(10)] # 11

all

ライブラリ procedure名 破壊 非破壊
sequtils all

boolを返すプロシージャを受け取り,配列のすべてに適用した結果がtrueであるならtrueを返します.

let seq = @[0, 1, 2, 3, 4, 5, 10, 11, 12, 13]

# 配列内のすべての数が正の数かどうかをチェック
echo seq.all(proc (x: int): bool = x > 0) # false

# 配列内のすべての数が非負の数かどうかをチェック
echo seq.all(proc (x: int): bool = x >= 0) # true

# templateを用いた省略形として,allItがあります。
echo seq.allIt(it >= 0) # true

any

ライブラリ procedure名 破壊 非破壊
sequtils all

boolを返すプロシージャを受け取り,配列内に結果がtrueとなる要素がある場合にtrueを返します.

let seq = @[0, -1, 2, 3, 4, 5, 10, 11, 12, 13]

# 配列内に負の数がいるかどうかをチェック
echo seq.any(proc (x: int): bool = x < 0) # true

# templateを用いた省略形として,anyItがあります。
echo seq.anyIt(it < 0) # true

apply

ライブラリ procedure名 破壊 非破壊
sequtils apply

配列内のそれぞれの要素にprocedurefを適用し、そのprocedureの返り値が並んだ同じ長さの新しい配列(例:[a_1, a_2, a_3, a_4] -> [f(a_1), f(a_2), f(a_3), f(a_4)])に変換します。ただし,fによって配列の要素の型を変更することはできません.mapの破壊版兼・型を変更できない版とも言えます。

var seq = @[0, -1, 2, 3, 4, 5, 10, 11, 12, 13]

# すべての数値をstringに変換します。
seq.apply(proc (x: var int) = x += 1)
echo seq # @[1, 0, 3, 4, 5, 6, 11, 12, 13, 14]

# templateを用いた省略形として,applyItがあります。
seq.applyIt(it + 1)
echo seq # @[1, 0, 3, 4, 5, 6, 11, 12, 13, 14]

concat

ライブラリ procedure名 破壊 非破壊
sequtils concat

同じ型を要素に持つ2つの配列を結合します。

let seq_1 = @[1, 2, 3]
let seq_2 = @[4, 5, 6]
echo concat(seq_1, seq_2) # @[1, 2, 3, 4, 5, 6]

# 配列が3つ、4つでも結合できます。
let seq_3 = @[7, 8, 9]
echo concat(seq_1, seq_2, seq_3) # @[1, 2, 3, 4, 5, 6, 7, 8, 9]

count

ライブラリ procedure名 破壊 非破壊
sequtils count

配列内に与えられた値がいくつあるかをカウントします

let seq = @[0, 0, 2, 3, 4, 4, 5]

# 配列内に4ががいくつあるかをチェック
echo seq.count(4) # 2

cycle

ライブラリ procedure名 破壊 非破壊
sequtils cycle

配列自身をn回繰り返して結合させたような新しい配列を生成します。

let seq_1 = @[1]
echo seq_1.cycle(5) # @[1, 1, 1, 1, 1]

let seq_2 = @[0, 1]
echo seq_2.cycle(3) # @[0, 1, 0, 1, 0, 1]

deduplicate

ライブラリ procedure名 破壊 非破壊
sequtils deduplicate

配列から重複を取り除いた新たな配列を返します。
配列がsortされている場合、trueを渡すことで配列がsortされていることを前提としたアルゴリズムにより処理を高速化することができます。

let seq_1 = @[1, 5, 4, 2, 2, 4, 3]
echo seq_1.deduplicate() # @[1, 5, 4, 2, 3]

let seq_2 = @[1, 1, 1, 1, 2, 3, 3, 4, 5, 5]
echo seq_2.deduplicate(true) # @[1, 2, 3, 4, 5]

distribute

ライブラリ procedure名 破壊 非破壊
sequtils distribute

配列をN個の配列に分割します。
第三引数にtrueを渡したとき(デフォルト)は,例えば学校のクラスでグループを作るときのように,最後の配列の要素数が小さくならないような調整が行われます。falseを渡したときは,例えば配列をN列に並べていくかのような挙動をします。

let seq = @[1, 2, 3, 4, 5, 6, 7, 8, 9 ,10]
echo seq.distribute(4) # @[@[1, 2, 3], @[4, 5, 6], @[7, 8], @[9, 10]]
echo seq.distribute(4, false) # @[@[1, 2, 3], @[4, 5, 6], @[7, 8, 9], @[10]]

filter

ライブラリ procedure名 破壊 非破壊
sequtils filter

配列にboolを返すprocedurefを適用し,結果がtrueとなる要素のみを保持する新たな配列を得ます。keepIfの非破壊版です。

# 偶数の要素のみからなる配列を生成する
let seq = @[1, 2, 3, 4, 5, 6, 7]
echo seq.filter(proc (x: int): bool = x mod 2 == 0) # @[2, 4, 6]

# 省略形としてfilterItがある
echo seq.filterIt(it mod 2 == 0) # @[2, 4, 6]

insert

ライブラリ procedure名 破壊 非破壊
sequtils insert
func insert[T](dest: var seq[T]; src: openArray[T]; pos = 0)

destのposの位置に、srcの配列を挿入します。

var seq_1 = @[0, 0, 0, 0, 0, 0]
let seq_2 = @[1, 1, 1]
seq_1.insert(seq_2, 2)
echo seq_1 # @[0, 0, 1, 1, 1, 0, 0, 0, 0]

keepIf

ライブラリ procedure名 破壊 非破壊
sequtils keepIf

配列にboolを返すprocedurefを適用し,結果がtrueとなる要素のみを保持するように配列を書き換えます。filterの破壊版です。

# 偶数の要素のみからなる配列にする
var seq = @[1, 2, 3, 4, 5, 6, 7]
seq.keepIf(proc (x: int): bool = x mod 2 == 0)
echo seq # @[2, 4, 6]

# 省略形としてfilterItがある
seq.keepItIf(it mod 3 == 0) 
echo seq  # @[6]

map

ライブラリ procedure名 破壊 非破壊
sequtils map

配列内のそれぞれの要素にprocedurefを適用し、そのprocedureの返り値が並んだ同じ長さの新しい配列(例:[a_1, a_2, a_3, a_4] -> [f(a_1), f(a_2), f(a_3), f(a_4)])に書き換えます。applyの非破壊版とも言えます。

let seq_1 = @[0, -1, 2, 3, 4, 5, 10, 11, 12, 13]

# すべての数値をstringに変換します。
let seq_2 = seq_1.map(proc (x: int): string = $x)
echo seq_2 # @["0", "-1", "2", "3", "4", "5", "10", "11", "12", "13"]

# templateを用いた省略形として, mapItがあります。
let seq_3 = seq_1.mapIt($it)
echo seq_3 # @["0", "-1", "2", "3", "4", "5", "10", "11", "12", "13"]

repeat

ライブラリ procedure名 破壊 非破壊
sequtils repeat
proc repeat[T](x: T; n: Natural): seq[T]

xn個並んだsequenceを返します。

let seq = repeat(0, 5)
echo seq # @[0, 0, 0, 0, 0]

zip

ライブラリ procedure名 破壊 非破壊
sequtils zip
proc zip[S, T](s1: openArray[S]; s2: openArray[T]): seq[(S, T)]

2つの配列をタプルの配列に変換します。

let tup = @([0, 1, 2], ["Tokyo", "Kanagawa", "Gunma"])
echo tup.zip # @[(a: 0, b: "Tokyo"), (a: 1, b: "Kanagawa"), (a: 2, b: "Gunma")]

foldl

ライブラリ procedure名 破壊 非破壊
sequtils foldl
template foldl(sequence, operation, first): untyped

2つの引数を受け取るproceduref(a, b)を用いて,配列に左から順にprocedureを適用していきます。ここで,f(a, b)の返り値は次の要素にaとして渡る値で,bは配列の要素です.また,第三引数にはaの初期値を渡します.

# foldlを使用して配列の合計を求めます
let seq_1 = @[1, 2, 3, 4, 5]
echo seq_1.foldl(a + b, 0) # 15

# foldlを使用して重み付け平均を求めます
let seq_2 = @[(1, 0.2), (2, 0.1), (3, 0.4), (4, 0.1), (5, 0.2)]
echo seq_2.foldl(a + float(b[0]) * b[1], 0.0) / seq_2.foldl(a + b[1], 0.0) # 3

foldr

ライブラリ procedure名 破壊 非破壊
sequtils foldr

2つの引数を受け取るproceduref(a, b)を用いて,配列に右から順にprocedureを適用していきます。ここで,f(a, b)の返り値は次の要素にaとして渡る値で,bは配列の要素です.また,第三引数にはaの初期値を渡します.

# foldrを使用して配列の合計を求めます
let seq_1 = @[1, 2, 3, 4, 5]
echo seq_1.foldr(a + b) # 15

newSeqWith

ライブラリ procedure名 破壊 非破壊
sequtils newSeqWith
template newSeqWith(len: int; init: untyped): untyped

[init, init, init, init, ...]と,initlen回繰り返された配列を生成します.
二次元の配列を生成するのに便利です。

let seq2D = newSeqWith(4, newSeq[int](4))
echo seq2D # @[@[0, 0, 0, 0], @[0, 0, 0, 0], @[0, 0, 0, 0], @[0, 0, 0, 0]]

toSeq

ライブラリ procedure名 破壊 非破壊
sequtils toSeq
template toSeq(iter: untyped): untyped

iterableな値をsequenceに変換します。

# Sliceをseqに
let slice = 1..6
echo toSeq(slice) # @[1, 2, 3, 4, 5, 6]

# setをseqに
let set = {1, 2, 3, 4}
echo toSeq(set) # @[1, 2, 3, 4]

リンク集・情報源

この記事の情報はすべて以下のサイトを参照しています。

更新履歴

  • 2020/8/7 公開

Discussion