目次

前書き

第1章 Nimをはじめよう

1.1 本書について
1.2 インストール
1.3 コンパイラ
1.4 Nimble
1.5 歴史
1.6 開発状況

第2章 基本文法

2.1 変数
2.2 データ型
2.3 制御構文
2.4 プロシージャ
2.5 イテレータ
2.6 モジュール
2.7 メタプログラミング

第3章 オブジェクト指向プログラミング

3.1 オブジェクトの定義
3.2 継承を行う
3.3 例外処理をする

第4章 パッケージ開発

4.1 パッケージの作成
4.2 パッケージの基本構造
4.3 依存パッケージをインストールする
4.4 実装

第5章 型

5.1 型クラス
5.2 浮動小数点型クラス
5.3 文字列型クラス
5.4 構造化型クラス
5.5 参照型クラス
5.6 ジェネリック型クラス
5.7 distinct型
5.8 auto型
5.9 型情報を得る
5.10 実行時型情報

第6章 プラグマ

6.1 プラグマを定義する
6.2 コンパイル時メッセージ
6.3 noSideEffect
6.4 compileTime
6.5 プログラムをストップさせる
6.6 構造体へのプラグマ
6.7 シャローコピー
6.8 列挙型の識別子強制
6.9 最適化
6.10 asmNoStackFrame
6.11 グローバル
6.12 警告抑制
6.13 実装固有のプラグマ

第7章 メタプログラミング

7.1 メタプログラミングとは
7.2 Nimのメタプログラミング
7.3 ドメイン固有言語
7.4 テンプレートとは
7.5 遅延評価
7.6 衛生性
7.7 章末問題
7.8 解答
7.9 マクロ
7.10 マクロを定義する
7.11 静的パラメータ
7.12 力を正しく利用する
7.13 プラグマ
7.14 文構造を分析する
7.15 抽象構文木
7.16 FizzBuzzを実装する
7.17 parseStmt
7.18 forマクロ
7.19 case ofマクロ
7.20 多重マクロ
7.21 Ast Pattern Matching

第8章 数値

8.1 数値リテラル
8.2 整数と浮動小数点数
8.3 算術演算
8.4 整数と浮動小数点数間の演算
8.5 乱数
8.6 Mersenne Twister
8.7 加算アルゴリズム

第9章 数学

9.1 有理数
9.2 複素数を扱う
9.3 統計分析

第10章 文字列

10.1 stringとcharとUnicode
10.2 cstring
10.3 文字列操作
10.4 パース
10.5 文字列の編集距離
10.6 文字列フォーマット
10.7 エンコード
10.8 ロープ
10.9 punycode

第11章 マルチスレッドプログラミング

11.1 threads
11.2 channels

第12章 ビット操作

12.1 基本的なビット演算
12.2 bitops

第13章 集合

13.1 HashSetを使う
13.2 OrderedSetを使う
13.3 SomeSetを使う

第14章 アルゴリズム

14.1 順列生成
14.2 逆順
14.3 ソート
14.4 探索
14.5 配列を埋める
14.6 分配法則
14.7 回転

第15章 動的配列処理

15.1 動的配列に変換する
15.2 多次元動的配列を作る
15.3 要素数を数える
15.4 要素のインデックスを得る
15.5 動的配列を結合する
15.6 配列の要素を繰り返す
15.7 ある要素を繰り返して配列を作る
15.8 重複を取り除く
15.9 ふたつの配列をまとめる
15.10 タプル配列を分解する
15.11 配列を分割する
15.12 要素を削除する
15.13 配列を挿入する
15.14 全ての要素に操作を行う
15.15 すべての要素に操作を行った配列を得る
15.16 条件を満たす要素のみの配列を得る
15.17 条件を満たさない要素を削除する
15.18 全ての要素が条件を満たすかどうかを得る
15.19 少なくともひとつの要素が条件を満たすかどうかを得る
15.20 リテラルに直接操作を適用する
15.21 畳み込み

第16章 外部関数インターフェイス

16.1 importc
16.2 C
16.3 プログラムを書く
16.4 C++
16.5 JavaScript

付録A コンパイラ

A.1 コマンド

付録B INim

B.1 インストール
B.2 使ってみる
B.3 オプション

前書き

 この度は『プログラミングNim』を手に取っていただき、ありがとうございます。

 私がNimと出会ったのは2018年、中学3年生の頃です。一見スクリプト言語のようで書きやすく、しかし高速で型に守られたこの言語は、一瞬で私を虜にしました。あれから3年も経ちましたが、現在でもメイン言語として、ライブラリやOSの開発に取り組んでいます。

 トランスパイル言語として、CやC++、Objective-C、JavaScriptに変換され、ネイティブなコンパイルは行わないという、思い切ったように見える方針のNim。他言語インターフェースによって資産を流用し、速度を担保し、その上でモダンな文法、表現力の高い様々な機能を実現しています。

 Nimは宣伝力に欠け、大企業による支援も多くは受けていないため、人々に、特に日本人の間ではあまり広まっていない印象があります。私はNimを広めるために、技術記事投稿サイトなどに知見を共有し始めましたが、体系性に欠けているため、同人出版することにしました。その後、運よく商業出版のお話をいただき、この本を書き上げることができました。

 『Nim in Action』という名著がある中で、商業出版をすることには少し迷いもありました。この本はもともとプログラミング初学者の方に、Nimを第一言語として使ってもらいたいという思いで書こうと考えていました。しかし蓋を開けてみると、思ったよりニッチな内容で占められてしまいました。そこで思い切って初心者向けの記述を全て省き、プログラミング経験者にNimの面白いところをいくつか知ってもらえればいいな、という方針に切り替えました。学ぶこと、というよりは読み物として、面白い知識などを十分含められたと思います。

 ぜひ、この本を通して、なんとなくNimって面白いな、興味が出たな、と思った方は、少しだけコードを書いてみてください。今まで書いていたスクリプト、Nimでささっと書くと、想像以上に高速化されるかもしれません。できあがったスクリプトは、Nimble package directoryに公開してみてください。この本をきっかけに日本人Nimmerが増えたとしたら、これほど喜ばしいことはありません。

お問い合わせ

 ご質問などがございましたら、こちらへご連絡ください。

 Twitter: @momeemt

 Gmail: momiimt@gmail.com

コミュニティの紹介

 私がDiscordで運営している「ゆるいNimサーバー」ですが、とてもゆるくNimmerが交流しています。2021年6月には、初のLT会が開催されました。よければ参加してみてくださいね。

 Discord: https://discord.gg/3umT33kQhS

第1章 Nimをはじめよう

 Nimは、2006年にAndreas Rumpf氏が開発を開始し、2008年に公開されたプログラミング言語です。2019年9月23日に、13年の時を経てversion 1.0がリリースされました。さまざまなバグや不具合が修正されたほか、サードパーティ製のライブラリがリリースされ、言語仕様もどんどん洗練されて、今日に至ります。

 公式サイトではEfficient, expressive, elegantと謳っているほど、効率的で表現豊かに、エレガントなプログラムを書くのにぴったりな言語です。

1.1 本書について

 本書は、技術書典10で頒布した「Nim XD Book1」「Nim XD Book2」を統合し、大幅に加筆修正を行った、プログラミング言語Nimについての技術書です。2021年6月、現在の最新バージョンであるversion.1.4.8を基に執筆しました。あらかじめ、本書の構成についての解説を行います。

 ・「Nimをはじめよう」では、本書の構成とNimの概要、環境構築、周辺ツールについて解説を行っています。

 ・「基本文法」では、Nimをすぐに書き始められるよう、最低限の文法事項について説明しています。

 ・「オブジェクト指向プログラミング」では、最低限に留めるべきとされるNimのオブジェクト指向機能について軽く説明しています。

 ・「メタプログラミング」では、Nimの最大の特徴ともいえる強力なメタプログラミングについて解説しています。

 ・「パッケージ開発」では、実際に手を動かしながら、Nimbleパッケージを開発する手順を説明しています。

 ・「型」では、静的型付け言語のNimにおける型について説明しています。

 ・「プラグマ」では、Nimで組み込みで実装されているプラグマの使い方について、大まかに触れています。

 ・「数値」では、Nimの数値リテラルから乱数などのトピックについて触れています。

 ・「数学」では、有理数や複素数、統計分析を扱う標準ライブラリについて触れています。

 ・「文字列」では、Nimの文字列の特徴や関連ライブラリについて触れています。

 ・「マルチスレッドプログラミング」では、Nimの低レベルマルチスレッドを扱うthreadとchannelについて触れています。

 ・「ビット操作」では、基本的なビット操作や関連ライブラリについて触れています。

 ・「集合」では、HashSetという効率的な集合について触れています。

 ・「アルゴリズム」では、標準ライブラリとして提供されているアルゴリズムについて触れています。

 ・「動的配列処理」では、seq[T] 型に対する扱い方や処理について触れています。

 ・「外部関数インターフェース」では、C言語の連携を中心に触れています。

 一貫した入門テキストというよりは、Nimに触れている方が、新しい機能やライブラリについての知見を深められるような構成にしています。サンプルソースもしっかり載せておりますので、リファレンスとしても活用しやすいかと思います。

1.2 インストール

 Unix系のオペレーションシステムを利用しているか、Windowsを利用しているか、その他のOSを利用しているかで方法が異なります。また、自動インストールと手動インストールがあり、基本的には前者が推奨されていますが、何か問題が発生した場合には、手動で環境を整えることも可能になっています。

1.2.1 Unix系OSで自動インストールする

 Nimのコア開発メンバーのdom96氏が開発した、choosenimを使ってインストールします。これは、複数の安定版と開発版を簡単に切り替えることのできる環境管理ツールです。

リスト1.1: choosenimのインストール

 1: curl https://nim-lang.org/choosenim/init.sh -sSf | sh

 choosenimのインストールと同時に、Nimの最新版がダウンロードされます。その後、パスを通してコマンドラインから呼び出せるようにしておきましょう。

リスト1.2: パスを通す

 1: export PATH=<HOME PATH>/.nimble/bin:$PATH

1.2.2 Windowsで自動インストールする

 Windowsの場合は、Nimの公式サイト1でインストーラーが配布されているので、それを用いてインストールします。起動すると、バイナリとNimのインストールディレクトリにパスを通すかどうかを聞いてくれるので、まとめてチェックしてください。また、NimはPCREsとOpenSSLに依存しているので、それらをインストールできる動的リンクライブラリをダウンロード2し、nim.exeと同じディレクトリに配置してから、インストーラーを起動してください。

1.2.3 パッケージマネージャーを用いてインストールする

 各OSに付属するパッケージマネージャーを用いて、インストールすることもできます。

ArchLinux

リスト1.3: ArchLinux

 1: pacman -S nim

Debian/Ubuntu

リスト1.4: Debian/Ubuntu

 1: apt-get install nim

Docker

 ユーザーコミュニティがコンパイラとNimbleが含まれたDockerイメージを管理しており、Docker Hubで公開されています。

リスト1.5: 安定版 Dockerイメージ

 1: docker pull nimlang/nim

リスト1.6: 開発版  Dockerイメージ

 1: docker pull nimlang/nim:devel

Fedora

リスト1.7: Fedora

 1: dnf install nim

FreeBSD

リスト1.8: FreeBSD

 1: pkg install nim

Mac OS

リスト1.9: Mac OS

 1: brew install nim

OpenBSD

リスト1.10: OpenBSD

 1: pkg_add nim

openSUSE

リスト1.11: openSUSE

 1: zypper in nim

Snap

リスト1.12: Snap 安定版

 1: snap install nim-lang --classic

リスト1.13: Snap LTS 1.0.x

 1: snap install nim-lang-lts-1 --classic

リスト1.14: Snap nightly

 1: snap install nim-lang-nightly --classic

Void Linux

リスト1.15: Void Linux

 1: xbps-install -S nim

1.2.4 手動インストールする

 何か問題があった場合には、手動でインストールすることもできます。自動インストールできた場合には、読み飛ばしていただいて構いません。

 まずは、公式サイトからx86_64バイナリ(64bit)3かx86バイナリ(32bit)4をダウンロードしてください。

 解凍して該当ディレクトリに移動したのち、次のコマンドを実行してください。

リスト1.16: インストールコマンド

 1: sh build.sh bin/nim c koch ./koch tools
2: bin/nim c koch
3: ./koch tools

 その後、binディレクトリと<HOMEPATH>/.nimble/binのパスを通してください。

 トランスパイルしたC言語のソースを実行するために、Cコンパイラを用意してください。macOSではデフォルトでclangというコンパイラが内蔵されているので、最新バージョンをインストールします。

リスト1.17: clangをアップデートする

 1: xcode-select --install

 Linuxでは、すでに何らかのコンパイラがプリインストールされているはずですが、ない場合はgccclangなどのコンパイラを用意してください。

1.3 コンパイラ

 Nimのコンパイラの大まかな使い方について解説します。

1.3.1 コンパイルする

 コンパイラの重要な機能のひとつとして、コンパイルがあります。

1.4 Nimble

 Nimbleは、Nimのパッケージ管理機能ビルド機能を兼ね備えたソフトウェアです。Nimのインストールと同時にNimbleもインストールされ、利用可能になります。まずは、使えるかどうかを確かめてみましょう。

リスト1.18: バージョンを確認する

 1: nimble --version

リスト1.19: 出力

 1: nimble v0.13.1 compiled at 2021-04-18 07:37:24

 もしここでNimbleがインストールされていなかった場合は、「付録C? Nimble」で入手方法を解説していますので、先にそちらを済ませてください。

1.4.1 Nimbleパッケージ

 Nimで書かれたライブラリやアプリケーションは、Nimbleを通して第三者に公開したり、第三者が書いたものをインストールできます。コマンドラインやGUIなどから利用するために、バイナリの状態で入手するパッケージをバイナリパッケージ、自分のNimプログラムから呼び出すために、ソースコードの状態で入手するパッケージをライブラリパッケージ、そのどちらも行うために、バイナリとソースコードを一緒に入手するパッケージをハイブリッドパッケージと呼んでいます。

 ここでは、バイナリパッケージをダウンロードして、動作させてみましょう。

リスト1.20: INimをインストールする

 1: nimble install inim

 バイナリは、~/.nimbleにダウンロードされているので、コマンドラインから利用するには、パスを通す必要があります。

1.4.2 プロジェクトを作成する

 Nimbleパッケージとして公開できるのは、Nimbleプロジェクトとして正しい構造でなければなりません。

リスト1.21: プロジェクトを作成する

 1: nimble init helloworld

1.5 歴史

 ここで、Nimやその周辺環境の歴史についても学んでおきましょう。

 NimはもともとNimrodという名前の言語でした。Nimrodは旧約聖書の登場人物で、『ユダヤ古代誌』では、バベルの塔の建設を命じた王であるとされています。ver.0.10.2のリリース時に、言語名がNimrodからNimに変わりました。

 もともと、コンパイルはPascalで実装されていました。2008年にセルフコンパイルが達成されて、現在はNimで書かれています。約90000行のコードで構成されており、さまざまな言語機能が実装されています。

 開発者であるAndreas Rumpf氏の当初の目標はC言語と同じくらい高速で、Pythonのように表現豊かで、Lispと同じくらい拡張性が高いことでした。具体的には、

 ・メタプログラミングによって言語を小さく保てること

 ・C言語にトランスパイルできること

 ・20000行のソースコードによって実装すること

 ・C言語やC++、Adaの失敗を活かすこと

 などが挙げられますが、15年経過した現在から見ると、おおよそ達成されているように見えます。Nimがどのように進化してきたかは、公式ブログ5でver.0.8.6(Nimrod時代)のリリース記録から知ることができます。

 2020年には、はじめての公式カンファレンスがオンラインで開催されました。15個の講演がYouTube6で配信され、現在も視聴できます。

 ・初心者のためのNimの紹介(Dominik Picheta氏)

 ・Nicoを用いてさっとゲームを開発する(Jez Impbox氏)

 ・INimが帰ってきました!(Tristram Oaten氏)

 ・埋め込み NimScript(Peter Munch-Ellingsen氏)

 ・低レベルなオーディオプログラミングをサポートするドメイン固有言語 Omni(Francesco Cameli氏)

 ・NimでLチカを実装する ESP8266プログラミング(Christian Jacobsen氏)

 ・Nimによるゲームボーイアドバンス開発(Jeremy Clarke氏)

 ・ARC/ORC(Andreas Rumpf氏)

 ・NimによるAndroid apkファイルのアセンブル Dali(Mateusz Czapliński氏)

 ・マルチスレッドプログラミング(Mamy André-Ratsimbazafy氏)

 ・Decimal128の使いどき・使い方(John Dupuy氏)

 ・動的サイトジェネレーター Gerbil(Jason Jones氏)

 ・Jesterプラグイン(John Dupuy氏)

 ・NimによるUI開発を考え直そう Fidget(Andre von Houck氏)

 ・3Dライブコーディング Enu(Scott Wadden氏)

1.6 開発状況

 Nimの開発状況を表1.1で見てみましょう。

表1.1: Nimのバージョン
バージョン 更新日時
ver.1.4.6 2021/04/15
ver.1.2.12 2021/04/15
ver.1.4.4 2021/02/23
ver.1.2.10 2021/02/23
ver.1.4.2 2020/12/01
ver.1.2.8 2020/10/27
ver.1.0.10 2020/10/27
ver.1.4.0 2020/10/16
ver.1.2.6 2020/07/30
ver.1.0.8 2020/07/30

 開発は活発で、おおよそ2か月程度でパッチリリースが公開されます。また、choosenimで開発バージョン(nightlies)を手に入れることができます。

リスト1.22: 開発バージョンのインストール

 1: choosenim devel

リスト1.23: ログ(執筆現在)

 1: Downloading Nim 2021-04-21-devel-eb221dcc27b7f8f11ec34046e3165508acdf8beb from GitHub
2: [##################################################] 100.0% 0kb/s
3:  Extracting nim-1.5.1-macosx_x64.tar.xz
4:     Setting up git repository
5:    Building Nim #devel
6:   Compiler: Already built
7:      Tools: Already built
8:   Installed component 'nimble'
9:    Switched to Nim #devel

 バージョンを確認すると、飛ばされている奇数バージョンはnightliesであることがわかります。

リスト1.24: 開発版のバージョンを確認する

 1: nim -v

リスト1.25: ログ(著者環境)

 1: Nim Compiler Version 1.5.1 [MacOSX: amd64]
2: Compiled at 2021-04-21
3: Copyright (c) 2006-2021 by Andreas Rumpf

1. https://nim-lang.org/install_windows.html

2. https://nim-lang.org/download/dlls.zip

3. https://nim-lang.org/download/nim-1.4.6_x64.zip

4. https://nim-lang.org/download/nim-1.4.6_x32.zip

5. https://nim-lang.org/blog.html

6. https://www.youtube.com/playlist?list=PLxLdEZg8DRwTIEzUpfaIcBqhsj09mLWHx

第2章 基本文法

 Nimは、空白文字(スペース)のインデントによってコードブロックが形成される、インデントブロックを採用しています。著名なプログラミング言語であるPythonもこれを導入しており、Nimを知らない多くのプログラマにとっては、NimがPythonのように映るかもしれません。しかし、決定的な違いは、Pythonが動的型付け言語であるのに対し、Nimは静的型付け言語であるという点です。近年ではコンパイル時に型が決定することの利点が注目され、Pythonでもver.3.5から型ヒントが実装されました。Nimの強力な点は、単に型を決定するだけでなく、コンパイル時定数やメタプログラミングで、複雑なコンパイル時計算が簡単に実行可能であることです。

 多くの教育機関で教材として採用されるなど、Pythonの学習ハードルの低さは周知の事実だと思いますが、Nimも入門するための言語仕様は、同等の難易度だと考えて差し支えありません。まずは本章で、先ほどインストールしたNimの基本的な文法事項について、勉強していきましょう。

2.1 変数

2.1.1 変数の定義

 ある識別子を与えて、識別可能になった書き換え可能な記憶域を変数といいます。それに対して、一度データを与えてしまうと書き換え不可能な記憶域を不変変数と呼びます。

 変数はvarを、不変変数はletを用いて、次のように定義できます。

リスト2.1: 変数と不変変数の定義

 1: var 変数名: データ型 = 値
 2: let 不変変数名: データ型 = 値

例題1

 変数名name、データ型がstring、値がmomiyamaである変数を定義しなさい。また、不変変数名value、データ型がint、値が100である不変変数を定義しなさい。

リスト2.2: 解答

 1: var name: string = "momiyama"
 2: let value: int = 100

 "momiyama"のような、ダブルクォーテーションで囲まれた字句要素を文字列リテラルといいます。また、'x'のように、シングルクォーテーションで囲まれた文字を文字リテラルと呼びます。これは、変数名などの識別子と文字や文字列を区別するために必要です。また、文字列リテラルの値のデータ型をstring、文字リテラルの値のデータ型をcharといいます。データ型については、この後説明します。

 100256.256などのように、直接数値を表す字句要素を数値リテラルと呼びます。小数点を含まない整数の数値リテラルのデータ型はint、小数のデータ型はfloatです。

練習1.

 変数名practice_1、データ型がstring、値がanswer_1である変数を定義しなさい。

2.1.2 標準入出力

 多くのコンピューターには、とくに明示しなければデフォルトで利用されるコンピューターへの入力・出力が存在します。これを標準入出力と呼びます。これからはキーボードによる打ち込みが標準入力、ターミナルやコマンドプロンプトへの出力が標準出力だと決められている環境下で話が進みますが、常にそうなるわけではありません。

標準出力

 標準出力とは、コンピューターがデフォルトでデータの出力先に指定する、出力装置や出力システムのことです。多くの汎用コンピューターではコンソールへの文字表示が設定されていますが、ファイルの書き出しなどに変更できます。echoプロシージャは、標準出力に書き込んでフラッシュflashする機能を提供します。

リスト2.3: echoプロシージャ

 1: echo "Hello, ", "world!"

リスト2.4: stdout

 1: Hello, world!

 echoは可変長パラメーターを受け取ります。その上、文字列に変換可能な任意の型のデータを受け取って、stringにキャスト(具体的にいえば$演算子を適用)して出力を行います。

リスト2.5: stringにキャストする

 1: echo 2, 0, 2, '1', "年 おめでとう!"

リスト2.6: stdout

 1: 2021年 おめでとう!

 echoは、JavaScriptバックエンドに対しても書くことができます1。ところで、デバッグに頻繁に用いられるため、スレッドセーフであることが特徴的です。とはいえIOなので、副作用であることには変わりなく、func文などにおいてデバッグすることができません。

 そのような場合は、debugEchoプロシージャを利用しましょう。

 echoプロシージャに対して、文字列リテラルだけでなくstring型の変数を指定することで出力できます。また、カンマで区切ることで、複数のシンボルを一気に出力できます。さらに、string型以外のデータに対しては暗黙に文字列型に変換され、出力できます。

リスト2.7: さまざまな出力

 1: var name: string = "momiyama"
 2: var id: string = "@momeemt"
 3:
 4: echo "name: ", name
 5: echo "id:   ", id
 6: echo "age:  ", 17

name: momiyama

id:   @momeemt

age:  17

練習2.

 標準出力に対し、Programming Language - Nimと出力しなさい。

標準入力

 標準入力から値を受け取るためには、stdinオブジェクトに対してreadLineプロシージャを呼び出します。

リスト2.8: 標準入力

 1: var input = stdin.readLine

2.1.3 型推論

 静的型付け言語にもかかわらず、型情報が見当たりません。Nimは、ユーザーに与えられた値から型を推測する型推論が実装されており、多くの場合で型宣言を省略できます。

リスト2.9: 型推論による変数定義

 1: var country = "Japan"
 2: let age = 17

 typeofプロシージャを使って、値の型が何かを調べることができます。変数countryと不変変数ageがどう推論されているか、確かめてみましょう。

例題2

 リスト2.9 における変数countryと不変変数ageの型を標準出力に出力しなさい。

リスト2.10: 解答

 1: echo typeof country
 2: echo typeof age

string

int

2.1.4 コンパイル時定数

 Nimには変数と不変変数の他にも、コンパイル時定数という記憶域を定義できます。

リスト2.11: コンパイル時定数の定義

 1: const コンパイル時定数名: データ型 = 値

 コンパイル時定数は、プログラムがコンパイルされるときに値が決定されます。つまり、コンパイル時に確定しない値を扱うことはできません。コンパイル時に確定しない値など、あるのでしょうか?たとえば、標準入力によって受け取る値は、実行時になるまで確定しません。そのような値を代入しようとした場合は、コンパイル時に静的エラーを検出します。

リスト2.12: コンパイル時に代入できない

 1: const inputConst = stdin.readLine

2.1.5 定義セクション

 複数の変数、不変変数、コンパイル時定数を定義セクションによってまとめて定義できます。

リスト2.13: 定義セクション

 1: var
 2:   country = "Japan"
 3:   name = "Momiyama"
 4:   id = "momeemt"
 5:   age = 17

 このように、インデントを空けてブロック構造を取るものをセクションといいます。

2.2 データ型

2.2.1 文字と文字列

 文字リテラルは、組み込み文字型であるchar型に型付けされます。

リスト2.14: char型

 1: const a: char = 'a'

 char型はしばしば、「1文字の文字」を扱う型であると勘違いされますが、正確には「1バイトの文字」を扱う型です。文字コード規格のUnicodeでは日本語が複数バイトで扱われるため、char型で日本語を保持することはできません。試しに代入してみましょう。

リスト2.15: charで平仮名を代入すると…

 1: const hiragana: char = 'あ'

リスト2.16: 標準エラー出力

 1: Error: missing closing ' for character literal

 1バイトで扱える文字は、アルファベットやいくつかの記号のみです。Nim言語でマルチバイト文字を処理する場合はUnicodeモジュールを利用しますが、これは本書の「」で解説します。

 文字列リテラルは、組み込み文字列型であるstring型に型付けされます。これはミュータブル、つまり値が変更可能であり、文字をあとから追加・書き換えができます。

リスト2.17: string型

 1: const greeting: string = "Hello!"

 また、三重符文字列リテラルによって、改行を含む文字列を扱うことができます。これはその名の通り「ダブルクォーテーションを3つ使って囲った文字列」であり、ドキュメント作成などに役立ちます。

リスト2.18: 三重符文字列リテラル

 1: const article: string = """
 2: I woke up early today.
 3: I dried the laundry.
 4: I bought a delicious lunch.
 5: I took a shower.
 6: A great day!
 7: """

結合

 &演算子を適用することで、文字や文字列を結合できます。

リスト2.19: 結合

 1: echo "Hi, " & "momeemt!"     # 文字列と文字列の結合
 2: echo "Hell" & 'o'            # 文字列と文字の結合
 3: echo 'G' & "ood morning!"    # 文字と文字列の結合
 4: echo 'A' & 'B'               # 文字と文字の結合

暗黙的な型変換は起こらない

 上のような結合演算を見て、「文字型と文字列型の間で暗黙の型変換が起こっているではないか!けしからん!」と思った方がいたかもしれませんが、型変換は起こっていません。

 「」で解説しますが、Nimの演算子は関数と同じようにオーバーロード可能で、ふたつの値を受け取って演算結果の値を返します。&演算子は、string型をふたつ受け取るものと、char型をふたつ受け取るものと、string型とchar型を受け取るものがそれぞれふたつで、4つの組み合わせで定義されています。

文字列の長さを調べる

2.2.2 整数と浮動小数点数

 数値リテラルは、整数ならばint型に型付けされます。負の整数を扱えるint型に対し、正の整数のみを扱う型を、uint型といいます。前者は「符号付き整数」と呼ばれ、後者は「符号なし整数」と呼ばれます。

 また、小数点を含む整数リテラルはfloat64型に型付けされます。

リスト2.20: 整数と浮動小数点数

 1: const
 2:   birthday: int = 2003_1030
 3:   number: float = 256.1024

四則演算

 整数や浮動小数点数には、四則演算が定義されています。

表2.1: 演算
演算 結果
+ 加算
- 減算
* 乗算
div 除算(整数)
/ 除算(浮動小数点数)
mod 剰余

 気をつけなければならないのは、除算です。整数の除算演算子 divでは、割り切れなかったとしても、値が切り捨てられて丸められます。浮動小数点数の除算演算子 /では、ある程度正確な小数値を返します。

2.2.3 真偽値

 真偽値は、「正しい」か「正しくない」を表現できる型です。「正しい」場合の値はtrueで、「正しくない」場合の値はfalseであり、ふたつの値だけを持ちます。

 真偽値型は、bool型に型付けされます。

リスト2.21: bool型

 1: const
 2:   value1: bool = true
 3:   value2: bool = false

関係演算子

 関係演算子を用いると、ふたつの値を受け取ってbool型の値を返します。整数型や浮動小数点数型、文字型や文字列型を比較できます。正確にいえば、ある型に関係演算子が定義されていれば、比較できます。

リスト2.22: 関係演算子

 1: echo 1 < 100   # 1は100より小さい : true
 2: echo 15 > 20   # 15は20より大きい : false
 3: echo 12 <= 99  # 12は99以下 : true
 4: echo 20 >= 48  # 20は48以上 : false
 5: echo 22 == 22  # 22は22と等しい : true
 6: echo 55 != 22  # 55は12と等しくない: true

論理演算子

 論理演算子は、高校数学の集合と同様に、論理式の真偽を返します。たとえば、条件Aと条件Bのどちらも正しいか?あるいはどちらかが正しいか?のような問題に対しての真偽を計算できます。

表2.2: 論理演算子
名前 演算子 効果
論理積 and すべての対象がtrueのときtrueを返します
論理和 or 対象のうち、少なくともひとつがtrueのときtrueを返します
排他的論理和 xor 一方がtrue、もう一方がfalseのときtrueを返します

論理否定 not trueならfalse、falseならtrueを返します

2.2.4 静的配列

 実行時に要素数を変更できない固定長コンテナーのことを、静的配列と呼びます。静的配列はデフォルトでarray型に型付けされます。配列は、複数の要素を格納・管理できるデータ構造で、大量のデータを扱う場合に、非常に便利です。

 arr[i]のように記述することで、配列arri番目にアクセスできます。このようなiのことを添字、またはインデックスといいます。ただし、添字は1からではなく0から始まるので、注意しなければなりません。

リスト2.23: 静的配列

 1: var arr: array[4, int] = [1, 2, 3, 4]
 2: arr[0] += 5
 3: arr[1] += arr[2]

 array型は、正確にはarray[I; T]型と定義されています。このようなブランケットに包まれたIT型変数といいます。Iは要素数を、Tは格納するデータの型を渡します。変数arrの型は、要素数が4、データ型がintであるarray[4, int]型ですが、これはarray[5, int]型やarray[4, string]型と明確に区別されます。

要素数を計算する

 静的配列に対しても、lenプロシージャを使って要素数を計算できます。

リスト2.24: 要素数を計算する

 1: var arr2: array[10, int] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
 2: echo arr2.len

2.2.5 動的配列

 実行時に要素数を変更できるコンテナーのことを、動的配列、またはシーケンスと呼びます。これはデフォルトでseq型に型付けされます。

リスト2.25: 動的配列

 1: var seq1: seq[int] = @[1, 2, 3, 4, 5]

 seq[T]型も、型変数Tを受け取って実体化します。seq[int]型とseq[string]型はまったく異なる型です。

要素数を増やす

 add

2.2.6 集合

 数学的な集合を扱うための型が、set[T]型です。ある型が取りうる範囲を全体集合と定めるため、型変数Tを受け取って実体化します。

 その型は、int8int16uint8byte-uint16charenum、またはそれに準ずる型でなければなりません。なぜなら、実装上の理由で全体集合が広すぎると、具体的には2^16個以上のときにエラーが発生します。

 もしそれより広い全体集合を取る集合を定義したい場合には、HashSet[T]型を使いましょう。第3章の「型システム」や第14章の「コレクション」で、詳しく説明します。

リスト2.26: アルファベットの集合

 1: var alphabet: set[char]
 2: alphabet = { 'a' .. 'z', 'A' .. 'Z' }
 3: echo alphabet

リスト2.27: stdout

 1: {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'}

2.3 制御構文

 制御構文は、条件分岐、ループに分けられます。

2.3.1 条件分岐

 条件分岐は制御構文のひとつで、ある処理を条件によって振り分けることができます。

if文

 if文はもっとも基本的な条件分岐構文です。bool型の値を条件に取り、trueの場合に、そのブロックに記述されたプログラムをすべて実行します。ifelifelseによって分岐し、その後の分岐条件に当てはまったとしても、それらは実行されません。

リスト2.28: if文の基本的な構造

 1: if condition1:
 2:   statement1
 3: elif condition2:
 4:   statement2
 5: else:
 6:   statement3

case文

 case文は、条件分岐構文のひとつです。caseofelseによって分岐します。if文とは違い、範囲を指定したり、カンマで区切って複数の値が指定できるなど、自由な条件分岐ができます。ただし、case文では、すべての値を網羅しない場合エラーになります。たとえば、文字列の値をすべて網羅することは不可能ですから、当てはまらない値に関しては、else句で実行する必要があります。

リスト2.29: case文

 1: let number = 0
 2: case number:
 3: of 0:
 4:   echo "zero :("
 5: of 90..99:
 6:   echo "almost perfect"
 7: of 100:
 8:   echo "perfect!"
 9: else:
10:   echo number

when文

 when文も、条件分岐構文のひとつです。

 しかし、if文やcase文とは、まったく毛色の異なる構文です。これらの条件分岐構文では、条件が実行時に評価されていました。しかし、when文ではコンパイル時に評価されます。そのため、条件は定数式でなければなりません。

 条件分岐に当てはまったブロックのみがコンパイルされ、その他の文は実行可能ファイルには残りません。このような特徴から、OS固有のコードや環境変数に依存するコードに用いられています。また、他の条件分岐構文と異なり、スコープは形成されません。

リスト2.30: when文

 1: when defined(widnows):
 2:   echo "windows"
 3: elif defined(macos):
 4:   echo "macos"
 5: elif defined(linux):
 6:   echo "Linux"
 7: else:
 8:   echo "unknown operation system :("

2.3.2 ループ

 ループは、ある処理を繰り返すことができる制御構文です。

 Nimのループでもっともポピュラーなのは、for文です。

for文

 for文は、繰り返し構造を持つデータから値を取り出し続け、なくなるまで処理を繰り返します。この繰り返し構造をイテレータと呼びますが、説明だけではイメージしづらいので、1から10までの整数を順番に出力する、単純なプログラムについて考えてみましょう。

リスト2.31: for文

 1: for i in countup(1, 10):
 2:   echo i

 countupイテレータは、ひとつ目の引数からふたつ目の引数までの値を持つ構造です。そこからまず1が取り出されて、ローカル変数iに代入されます。10まで取り出されると空っぽにになるので、その時点でループは終了します。

 また、countupイテレータは..イテレータというエイリアス(別名)を持ちます。より直感的な構文で記述できます。

リスト2.32: エイリアス

 1: for i in 1..10:
 2:   echo i

while文

 while文は、もっとも単純なループ制御構文です。for文のように、イテレータやローカル変数を持ちません。

リスト2.33: while文

 1: var counter = 0
 2: while counter <= 10:
 3:   echo "yeah!"
 4:   counter += 1

break文

 break文は、スコープから抜けるための制御構文です。

リスト2.34: break文

 1: for i in 27..100:
 2:   if i mod 8 == 0:
 3:     break
 4:   echo i

27

28

29

30

31

 デフォルトでは現在のスコープのみを抜けますが、ラベルを明示することで、そのラベルを持つスコープから抜けることができます。

リスト2.35: ラベルを抜ける

 1: block block1:
 2:   for i in 7..10:
 3:     block block2:
 4:       for j in 13..22:
 5:         if (i * j) mod 6 == 0:
 6:           break block1
 7:         echo i * j

91

98

105

112

119

試し読みはここまでです。
この続きは、製品版でお楽しみください。