【マイナー言語紹介】IDL（Interactive Data Language）とGDL（GNU Data Language）入門

この記事は[**IDL**（Interactive Data Language）](https://ja.wikipedia.org/wiki/Interactive_Data_Language)と**GDL**（GNU Data Language）というプログラミング言語を紹介して簡単に使い方を説明します。

なお、読者が他のプログラミング言語を勉強したという前提で書いているので、ここでプログラミングの基本を触れません。


# IDLとは

**IDL**は多分あまり誰も知らないマイナー言語だと思います。qiitaでもIDLの記事は殆ど見かけません。ただし特定の業界ではまだ活躍しているので、知り甲斐知っておいて損はないかもしれません。

::: note warn
[**インタフェース記述言語**（Interface Description Language）](https://ja.wikipedia.org/wiki/インタフェース記述言語)も同じく「**IDL**」と略されますが、これはプログラミング言語ではない、全く別のものであり、混同してはなりません。
qiitaでも[「**IDL**」で検索したら](https://qiita.com/search?q=IDL)この2つは混在しているという状態になって困っていますね。
:::

IDLは1977年から生まれてかなり古い歴史を持っています。配列の計算を簡単にできるのは特徴で、それに加えてグラフィックの機能も持っています。

配列の計算はPythonのnumpyやMATLABと似ていますが、IDLはずっと先に生まれて（大体C言語のすぐ後）、あの時代にとってこれは優秀とも言えるでしょう。

古い時代の言語にしては、新時代の言語と似ているところが多い。しかも最初から科学研究を主として作られたので、特に外部ライブラリーをインストールする必要もなく元から必要な関数は充実している。

残念なのは、IDLが**有料**で使うのにライセンスが必要なので誰も使えるわけではない。これはIDLが人気出ない一番の理由でしょう。

現在は天文業界でまだIDLを使う場面が多少ありますが、どんどんPythonに乗り換えられつつあるから衰退して忘れ去られていくでしょう。

それは仕方ないことですね。有料の上にどの機能もPythonができるから。例えば配列の計算はnumpyで、グラフィックの機能はmatplotlib。更にPythonを使うとpandasやopencvなど便利なモジュールと簡単に連携できますし。

それでも古くから存在しているコードはIDLで書いたものが多いので、少なくとも解読できる程度のIDLの知識が役に立つのです。

私もIDLコードをPythonに書き換えるという作業をしていたので、その段階で習得したのです。


# GDLとは

**GDL**（GNU Data Language）は簡単にいうと**IDLの無料版**です。IDLが有料なので使うにはライセンスが必要で不便ですが、実はお金を払いたくないなら代わりにGDLを使えばいいという話になります。

GDLを使う時は「IDLを使っている」と認識してもいい。違うのは「GDLはIDLの古いバージョンの機能しか持っていない」ということです。そのせいで新しく追加された関数などは使えません。

ただし一部の新しい関数は実はコードがサイトで公開されているので、自分でダウンロードして追加ライブラリーとして使うことができるから、ちょっと工夫が必要であるものの何とかできる場合が多いです。

この記事で以下説明する内容も全部GDLで実行する結果を使うので、これを読んで興味を持って自分で実行したいならインストールしてすぐできます。

紹介する入門の内容も勿論IDLでもGDLでも同じようにできることなので、違いを認識する必要はありません。


# インストールと実行

IDLは有料なので、ここでIDLのインストールに関して触れませんが、GDLのインストール方法は意外と簡単だから誰でもすぐ使用できます。

このリンクからインストール用のファイルをダウンロードして実行したら完了。（Windowsでは.exe、Macでは.dmgファイル）
https://github.com/gnudatalanguage/gdl/releases

PATHを設定して「gdl」を実行したらgdlの対話式シェルでGDLを実行できます。


# 基本文法

## はじめに

まずは定番の挨拶の言葉から
```idlang
print, 'hallo welt!'
```

`$`で始めたらgdlのシェル内からコマンドラインを実行することができます。

例えばこう書いたらpythonを実行することもできます。
```idlang
$python xxx.py
```

## 関数とプロシージャ

IDLでは2つ似ているものがあります。
- 関数（function）
- プロシージャ（procudure）

どれも他の言語の関数に似ていますが、使い方は違います。

例えば`print`みたいなのは関数ではなくプロシージャで、使い方はこう：
```
プロシージャ名, 引数1, 引数2, ...
```
引数と同等に並んでいるように見えてちょっとわかりにくい気もしますね。

関数の場合、殆どの言語と同様な使い方：
```
関数名(引数1, 引数2, ...)
```

似ているようにものが2つあって混同しやすくてややこしいのですが、使い分けを認識する必要があります。

数学の計算などは全部関数なので直感的です。
```idlang
cos(3.142)
```
```:結果
     -0.99999994
```

プロシージャと違って関数は返り値を持つし、重ねたり並べたり複雑に書くこともできます。

プロシージャも関数も、引数の他にキーワードも加えることができる。例えば`print`は`format`というキーワードで表示する書式を指定できます。
```idlang
print, 1.2, format='(f11.6)'
```
```:結果
   1.200000
```


## コメントアウト

IDLのコメントアウトは`;`を使います。
```idlang
print, 'koko wa shutsuryoku sareru' ; kono ato wa imi nai
```

ただし数行のコメントアウトの方法はありません。


## 変数

変数は宣言する必要なく、すぐ代入して使えます。使える文字はラテン文字の`a-z`と数字と`_`。ただし大文字と小文字の区別がありません。数字で始まることはできません。

例：
```idlang
_aa1 = 10
print, _AA1
```

結果
```
10
```

変数のデータ型は代入の時に自動的に決まります。

::: note info
IDLの変数名に使えない[予約語](https://ja.wikipedia.org/wiki/予約語)：
and　begin　break　case　common　compile_opt　continue　do　else　end　endcase　endelse　endfor　endforeach　endif　endrep　endswitch　endwhile　eq　for　foreach　forward_function　function　ge　goto　gt　if　inherits　le　lt　mod　ne　not　of　on_ioerror　or　pro　repeat　switch　then　until　while　xor
:::


## データ型を調べる方法

`help`プロシージャを使えば変数のデータ型はわかります。
```idlang
aa = 1
bb = 1.
help, aa
help, bb
help, '1'
```
```:結果
AA              INT       =        1
BB              FLOAT     =       1.00000
<Expression>    STRING    = '1'
```

ここで変数ではない場合`<Expression>`となります。


## 数字のデータ型

古い時代の言語と同様にIDLデータ型は基本的に単なる「整数」（int）と「浮動小数点数」（float）ではなく、細かく分けられます。

整数の種類は以下
| データ型 |範囲 | 例 |
|:-:|:-:|:-:|
| BYTE | 0 ~ 255 | 4B |
| INT | –32,768 ~ 32,767 | 4 |
| UINT | 0 ~ 65535 | 4L |
| LONG | –2,147,483,648 ~ 2,147,483,647 | 4L |
| ULONG | 0 ~ 4,294,967,295 | 4UL |
| LONG64 | -9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807 | 4LL |
| ULONG64 | 0 ~ 18,446,744,073,709,551,615 | 4ULL |

単に数字を書いたら自動的にINT型になりますが、その他にしたい場合B、L、ULなどを付けるのです。それともデータ型の名前と同じ関数を使うことで作ることもできます。

```idlang
help, 101LL
help, ulong(102)
```
```:結果
<Expression>    LONG64    =                    101
<Expression>    ULONG     =          102
```

浮動小数点数は2種類あります。

| データ型 |範囲 | 例 |
|:-:|:-:|:-:|
| FLOAT | ±10<sup>38</sup> | 4. |
| DOUBLE | ±10<sup>308</sup> | 4.D |

その他に複素数もあります。

| データ型 |要素 | 例 |
|:-:|:-:|:-:|
| COMPLEX | (FLOAT, FLOAT) | complex(4,5) |
| DCOMPLEX | (DOUBLE, DOUBLE) | dcomplex(4,5) |

使う例：
```idlang
sqrt(complex(-2,0))
```
```:結果
(       0.0000000,       1.4142135)
```


## データ型の変換

基本的にデータ型と同じ名前の関数を使えば簡単にデータ型を変えられます。ただしINTの場合`int`ではなく`fix`を使うので注意。
```idlang
help, float(20)
help, fix('50')
help, string(10000)
```
```:結果
<Expression>    FLOAT     =       20.0000
<Expression>    INT       =       50
<Expression>    STRING    = '   10000'
```

`fix`関数はキーワード`type`を付けることでどの型のデータにも変換できます。（付けない場合はINT）

ただし書くのはそのデータ型の番号。各データ型の番号は：
| データ型 | 番号 |
|:-:|:-:|
| UNDEFINED | 0 |
| BYTE | 1 |
| INT | 2 |
| LONG | 3 |
| FLOAT | 4 |
| DOUBLE | 5 |
| COMPLEX | 6 |
| STRING | 7 |
| STRUCT | 8 |
| DCOMPLEX | 9 |
| POINTER | 10 |
| OBJREF | 11 |
| UINT | 12 |
| ULONG | 13 |
| LONG64 | 14 |
| ULONG64 | 15 |

例：
```idlang
x = 1.1
help, fix(x, type=7)
```
```:結果
<Expression>    STRING    = '      1.10000'
```


## 数学の計算

基本的な計算は他の言語と似ています。
| 表記 | 意味 | 例 |
|:-:|:-:|:-:|
| + | たし算 | 1+2 |
| - | ひき算 | 2-1 |
| * | かけ算 | 2*3 |
| / | わり算 | 4/2 |
| ^ | 累乗 | 3^2 |
| mod | 合同式 | 4 mod 3 |

ただし注意するべきはわり算の場合データ型は自動的に整数から小数へ変わりません。

```idlang
a = 3/2
b = 3/2.
help, a
help, b
```
```:結果
A               INT       =        1
B               FLOAT     =       1.50000
```

C言語やJavaScriptみたいに`+=`と`++`という書き方も使えます。

```
a = 2
a += 5
a++
print, a
```
```:結果
       8
```


## 数学関連の関数

よく使う数学関数は大体揃っています。

| 関数 | 意味 |
|:-:|:-:|
| abs(x) | 絶対値 |
| floor(x) | x以下の最大整数 |
| ceil(x) | x以上の最小整数 |
| round(x) | xに一番近い整数 |
| sqrt(x) | 平方根 |
| exp(x) | 指数関数 |
| alog(x) | 自然対数 |
| alog2(x) | 底2の対数 |
| alog10(x) | 常用対数 |
| sin(x)<br>cos(x)<br>tan(x)  | 三角関数 |
| asin(x)<br>acos(x)<br>atan(x[, y])  | 逆三角関数 |
| sinh(x)<br>cosh(x)<br>tanh(x)  | 双曲線関数 |

`atan`は引数を2つ入れることで[atan2](https://ja.wikipedia.org/wiki/atan2)みたいな使い方になれます。


## 論理演算子

面倒なことに、IDLの論理演算子他の言語みたいな`==`、`!=`、`>`、`<`ではなく、全部ラテン文字で書きます。これは気をつけなければ起こしやすいミスです。
| 演算子 | 意味 | 他の言語 |
|:-:|:-:|:-:|
| eq | 等しい | == |
| ne | 異なる | != |
| le | 等しいかより小さい  | <= |
| lt | より小さい | < |
| ge | 等しいかより大きい | >= |
| gt | より大きい  | > |

真偽の結果はBYTEというデータ型になって、偽なら`0`か真なら`1`という値になります。`true`と`false`に表示されることはないのでわかりにくいかもしれません。

```idlang
help, 1 gt 1
help, 1 eq 1
```
```:結果
<Expression>    BYTE      =    0
<Expression>    BYTE      =    1
```

`and`と`or`はPythonなどと同じ。
```idlang
help, (1 eq 1) and (1 gt 1)
help, (1 eq 1) or (1 gt 1)
```
```:結果
<Expression>    BYTE      =    0
<Expression>    BYTE      =    1
```

真偽を逆にしたい場合は`~`を使います。
```idlang
~(1 lt 2)
~(1 gt 2)
```
```:結果
0
1
```

## システム定数

IDLの中で色々よく使う定数が準備されていのですぐ使えます。その定数はどれも`!`から始めるものです。定数なので勝手に書き換えることはできません。

例えば：
| 変数 | 意味 |データ型|
|:-:|:-:|:-:|
| !pi | FLOAT型の円周率 | FLOAT |
| !dpi | DOUBLE型の円周率 | DOUBLE |
| !dtor | ラジアン/度 = 0.0174533 | FLOAT |
| !true | 1 | BYTE |
| !false | 0 | BYTE |
| !null | 何もない | UNDEFINED |
| !values.f_nan | FLOAT型のNaN | FLOAT |
| !values.d_nan | DOUBLE型のNaN | DOUBLE |
| !values.f_infinity | FLOAT型の無限大 | FLOAT |
| !values.d_infinity | DOUBLE型の無限大 | DOUBLE |

`!true`と`!false`はただ数字の`0`と`1`ですが、可読性のために使うこともあります。

NaN（Not a Number）はマイナスの数字の平方根を探す時などに現れる値です。

無限大（infinity）はFLOATが`0`でわる時などに現れる値です。（ただしINTの場合はエラー）

また、`!path`などパス変数も`!`だが定数ではなく、書き換えられる変数です。


## 文字列

文字列は`' '`か`" "`で囲むことで作れます。それとも`string`関数で数字などから作ることもできます。ただしこのまま使うと空白がたくさん付いてくることになります。例えばINTでは長さ8、FLOATは長さ13の文字列となります。
```idlang
help, string(10)
help, string(10.1)
```
```:結果
<Expression>    STRING    = '      10'
<Expression>    STRING    = '      10.1000'
```

そうならないように`format`キーワードに書式を入れることができます。（`print`の時と同様）
```idlang
help, string(10, format='(i2)')
help, string(10.1, format='(f5.2)')
```
```:結果
<Expression>    STRING    = '10'
<Expression>    STRING    = '10.10'
```

ここでの書式はFortranと同じものなので説明は割愛しますが、Fortran関連の記事を参考に：
- [【Fortran基礎③】read文・write文での書式](https://honey-bears-net.com/fortran-format/)
- [6. 書式指定・ファイル入出力・文字列処理 &mdash; Fortran演習(地球惑星物理学演習)](https://amanotk.github.io/fortran-resume-public/chap06.html)
- [Fortran 入門: データ入出力](https://www.nag-j.co.jp/fortran/FI_14.html)

文字列は`+`を使うことで連結できます。
```idlang
s = 'raamen'
print, 'miso'+s
s += 'ya'
print, s
```
```:結果
misoraamen
raamenya
```

IDLでは文字列に使う便利関数がたくさんあります。例えば：
| 関数 | 機能 |
|:-:|:-:|
| strlen(s) | 文字列の長さ |
| strpos(s1, s2) | 文字列s1の中での文字列s2の位置を探す |
| strmid(s, i, n) | i-1番目の文字からn字取る |
| strtrim(s) | 前後にある空白を消す |
| strcompress(s) | 全ての空白を消す |
| strcmp(s1, s2) | 2つの文字列を比較する |
| strmatch(s, p) | 文字列sから指定のパターンpを探す |
| strupcase(s) | 大文字に変換する |
| strlowcase(s) | 小文字に変換する |

C言語などみたいに`[ ]`で文字列の中の文字を取ることはできない。その代わりに`strmid`が使われます
```idlang
s = 'yakisoba'
print, strmid(s, 4, 1)
print, strmid(s, 1, 3)
```
```:結果
s
aki
```


# ソースファイルの実行（＝プロシージャの使い方）

IDLは基本的に対話式シェルで実行するので、コマンドラインからコードファイルを実行することはできません。その代わりに対話式シェルの中でファイルの名前を打てばプロシージャとして実行することになります。

## プロシージャの作り方
IDLソースファイルの拡張子は`.pro`です。これもprocedureの略。

プロシージャのファイルの書き方：
```ファイル名.pro
pro ファイル名

実行する内容

end
```

例えばこういうファイルを保存します。
```tesuto.pro
pro tesuto
print, 'kono procedure wa imi nai'
end
```

そして対話式シェルでそのファイルの名前で呼び出す。
```idlang
tesuto
```
```:結果
kono procedure wa imi nai
```

引数がある場合はこう書きます。
```ファイル名.pro
pro ファイル名, 引数1, 引数2, ...

実行する内容

end
```

例：
```tesuto2.pro
pro tesuto2, aaa, bbb
print, aaa+bbb
end
```
実行
```idlang
tesuto2, 3, 4
```
```:結果
       7
```

## 引数とキーワードの使い分け

以上の引数は書いた順番で値の代入を決める引数であり、「位置引数」と呼びます。その他にも名前付き引数、つまり「キーワード」というものも使えます。その場合こう書きます
```ファイル名.pro
pro ファイル名, キーワード1=キーワード1, キーワード2=キーワード2, ...

実行する内容

end
```

例：
```tesuto3.pro
pro tesuto3, a=a, b=b
print, a*b
end
```
実行
```idlang
tesuto3, a=4, b=5
```
```:結果
      20
```

位置引数とキーワードは一緒に使うことができます。先に位置引数を書いて、その後はキーワード。
```ファイル名.pro
pro ファイル名, 位置引数1, ..., キーワード1=キーワード1, ...

実行する内容

end
```


## 引数とキーワードの省略

引数もキーワードもどれも省略してもいいですが、その場合`keyword_set`という関数でその引数やキーワードの値が入力されたかチェックすることが多い。返り値は`0`（偽）と`1`（真）。

例えばこんなプロシージャ
```tesuto4.pro
pro tesuto4, a
print, keyword_set(a)
end
```
キーワードが渡された場合
```idlang
tesuto4, 1
```
```:結果
1
```

キーワードが渡されなかった場合：
```idlang
tesuto4
```
```:結果
0
```

こんな風に条件分岐と一緒に使うことで引数かキーワードが渡された場合とない場合を使い分けできます。


## 変数に値を与えるプロシージャ

プロシージャに渡される引数かキーワードはそのプロシージャに値を与える役目の他に、逆にプロシージャの中から値をもらうという形になることもあります。

例えばこんなプロシージャ：
```tesuto5.pro
pro tesuto5, ggg
ggg = 'konnichiwa'
end
```
使う時に：
```idlang
tesuto5, k
print, k
```
```:結果
konnichiwa
```

つまりここで変数`k`（プロシージャ内で`ggg`）はプロシージャに渡されたことで文字列をもらったのです。

この場合プロシージャに渡された変数は**予め宣言する必要がなく**、プロシージャが起動した時点で新しく定義されるのです。

こんな方法は特にPythonの使い手にとって見慣れないかもしれないが、IDLのプロシージャや関数でよく使われるので理解しておかないと悶々としてしまいそうですね。

勿論、既存の変数でもプロシージャ内へ渡されたことで値が変わることもあるので、これも注意。
```nibai.pro
pro nibai, x
x *= 2
end
```
使う時に。
```idlang
x = 3
nibai, x
print, x
```
```:結果
       6
```


## キーワードの前の「/」（スラッシュ）

これはIDL独特の書き方とも言えるので特に注目したいです。

例えばこんなプロシージャがあります。
```tesuto6.pro
pro tesuto6, h=h
print, 'h = ', h
end
```

このように実行したら
```idlang
tesuto6, /h
```
こうなります。
```:結果
h =        1
```

これはつまり`/h`は`h=1`と同じ意味です。

IDLの中でのプロシージャと関数は、特にキーワードの値が真偽として扱う場合は、こんな使い方をするものが多いので、理解しておく必要があります。


# 関数の作り方

関数はプロシージャと似ている方法で書けます。同じように`.pro`のファイルに書くことで呼び出せます。

書き方：
```関数名.pro
function 関数名, 引数1, 引数2, ..., キーワード1=キーワード1, キーワード2=キーワード2, ...
    実行したい内容
    return 返り値
end
```

プロシージャとの違いはただ`pro`を`function`に変えることと、`return`を書いて返り値わ与えられること。

`return`がなくても問題ないが、その場合返り値は`0`となります。

引数とキーワードの使い方もプロシージャと同じ。

例：
```menseki.pro
function menseki, hirosa, takasa
    return hirosa*takasa
end
```
実行
```idlang
menseki(6,7)
```
```:結果
      42
```


# 配列
配列がIDLの一番の長所とも言えるので詳しく説明します。

## 配列の基本

配列を作る一番基本的に方法は`[ ]`で囲むことです。
```idlang
arr = [5,7,5]
```

中身の要素を使う時にインデックスを`[ ]`に入れて後ろに書く。ただしインデックスは`0`から数える。（CやPythonやRubyなどと同様。FortranやMATLABなどと違う）
```idlang
arr = [2,8,11]
print, arr[0]
```
```:結果
       2
```

負の数を使って後ろから数えて指定することもできます。（最後の要素は`-1`）
```idlang
arr = [3,5,7,9]
print, arr[-2]
```
```:結果
       7
```

`:`で範囲を指定して一部使うこともできます。
```idlang
arr = [3,3,4,4,5,5]
print, arr[1:4]
```
```:結果
       3       4       4       5
```

注意：Pythonと違って、後ろの数字まで含まれます。先程の例ではPythonなら`3 4 4`になるはず。

そしてPythonみたいに`[:3]`、`[2:]`みたいな書き方はできず、`[0:2]`、`[2:*]`と書く必要があります。

ステップで要素を取る場合こう書くこともできます。
```idlang
arr = [0,1,2,3,4,5]
print, arr[0:*:2]
```
```:結果
       0       2       4
```

（Pythonでの`[::2]`と同じ）

ただしPythonみたいにマイナス値を使うことはできない。`[0:*:-1]`はエラー。順番を逆にしたいなら`reverse`という関数を使います。

配列をインデックスにして指定の順番の複数の要素を取ることもできます。
```idlang
arr = [12,13,14,15,16]
print, arr[[1,3]]
```
```:結果
      13      15
```


## 配列の要素の書き換え

このように配列の中の要素を書き換えることもできます。
```idlang
arr = [2,2,2,2,2,2]
arr[1] = 3
print, arr
arr[2:4] = 5
print, arr
```
```:結果
       2       3       2       2       2       2
       2       3       5       5       5       2
```

`[*]`で全ての要素を同じ値にすることもできます
```idlang
arr = [1,5,9,0]
arr[*] = 2
print, arr
```
```:結果
       2       2       2       2
```


## 二次元以上の配列

`[ ]`を重ねることで二次元以上の配列も作れます。書く時一行で書くよりも改行した方がいいかもしれないが、その場合最後に`$`を付ける必要があります。

例：
```idlang
arr = [[1,0,1],$
       [0,5,0],$
       [1,0,1]]
print,arr
```
```:結果
       1       0       1
       0       5       0
       1       0       1
```

中身の要素を使う時に`[列,行]`のように書きます。順番はPythonと逆なので注意。

```idlang
arr = [[0,1,2],$
       [3,4,5],$
       [6,7,8]]
print, arr[2,1]
```
```:結果
       5
```

ただし一つだけ数字を`[ ]`に入れる場合は全体から数える順番で要素を取ることになります。
```idlang
print, arr[7]
```
```:結果
       7
```

欲しい列の全ての要素を取りたい場合は`[列,*]`と書く。
```idlang
print, arr[2,*]
```
```:結果
       2
       5
       8
```

欲しい行の全ての要素を取りたい場合は`[*,行]`と書く。
```idlang
print, arr[*,2]
```
```:結果
       6       7       8
```


## 配列の計算

配列の間の計算はその中の各要素の計算となります。
```idlang
arr1 = [[1,1,1],$
        [2,2,2]]
arr2 = [[2,2,2],$
        [5,5,6]]
print,arr1+arr2
```
```:結果
       3       3       3
       7       7       8
```

ただしサイズが違う場合、小さい方の配列だけの計算になって、そのサイズの配列が返ってくる。（この場合Pythonならエラーだが）
```idlang
arr1 = [[1,2,3,4,5]]
arr2 = [[10,10]]
print, arr1+arr2
```
```:結果
      11      12
```

これは二次元の配列の場合も使えますが、次元と関係ない順番で計算してしまってなんかややこしくなるので、あまり使わない方がいいかも。
```idlang
arr1 = [[1,2,3],$
        [4,5,6]]
arr2 = [[3,3],$
        [3,3]]
print, arr1-arr2
```
```:結果
      -2      -1
       0       1
```

配列と普通の数字の計算もできます。
```idlang
print, [1,2,3]+2
```
```:結果
       3       4       5
```


## 行列の乗法
二次元以上の配列は`#`演算子で行列みたいなかけ算ができます。これは普通各要素のかけ算に使う`*`と違うので注意。

例：
```idlang
arr1 = [[1,2,3],$
        [4,5,6]]
arr2 = [[1,2],$
        [3,4],$
        [5,6]]
print, arr1#arr2
```
```:結果
           9          12          15
          19          26          33
          29          40          51
```

それとも`matrix_multiply`という関数でも同じような計算ができます。

```idlang
print, matrix_multiply(arr1, arr2)
```

その他にも`matrix_power`関数で正方行列の自分とのかけ算ができます。例えば`matrix_power(arr)`は`arr#arr`と同じ。


## 配列のデータ型

配列の中身はどのデータ型でもいいが、一つの配列では全部同じデータ型となります。違うものを入れた場合は自動的に同じように変換されます。例えばINTとFLOATが混在したら全部FLOATとなります。

```idlang
print, [1,2,3.]
```
```:結果
      1.00000      2.00000      3.00000
```

`help`で中のデータ型を調べることができます。

```idlang
help, [1,2,3]
```
```:結果
<Expression>    INT       = Array[3]
```

データ型の変換は単体のデータと同じく、そのデータ型と同じ名前の関数で変換できます。
```idlang
arr = [1.2,2.3,3.4,4]
arr = long(arr)
print, arr
help, arr
```
```:結果
           1           2           3           4
<Expression>    LONG      = Array[4]
```

## 配列のサイズを調べる方法

配列のサイズに関する情報は`size`関数でできます。
```idlang
arr = [[0,1,0,1],$
       [1,0,1,0],$
       [0,1,0,1]]
print, size(arr)
```
```:結果
           2           4           3           2          12
```

二次元の配列の場合は数字5つ出てきますが、意味は：
- 配列の次元
- 列の数
- 行の数
- データ型の番号
- 全部の要素の数

一次元の配列なら数字が4つになります。

各軸の数だけ調べたい場合`/dimensions`キーワードを付けます。
```idlang
print, size(arr, /dimensions)
```
```:結果
           4           3
```

全ての要素の数だけ調べたいなら`n_elements`関数を使います。
```idlang
print, n_elements([1,2,3,4])
```
```:結果
           4
```


## 配列を作る関数

`[ ]`で作る以外に、配列は色んな関数から作成できます。

例えば中身全部0の指定のサイズの配列を作る場合はこれらの関数を使います。

| データ型 | 関数 |
|:-:|:-:|
| BYTE | bytarr |
| INT | intarr |
| UINT | uintarr |
| LONG | lonarr |
| ULONG | ulonarr |
| LONG64 | lon64arr |
| ULONG64 | ulon64arr |
| FLOAT | fltarr |
| DOUBLE | DBLARR |

例えば：
```idlang
print, fltarr(3,2)
```
```:結果
      0.00000      0.00000      0.00000
      0.00000      0.00000      0.00000
```

値が並ぶ配列が欲しい場合これらの関数を使います。

| データ型 | 関数 |
|:-:|:-:|
| BYTE | bindgen |
| INT | indgen |
| UINT | uindgen |
| LONG | lindgen |
| ULONG | ulindgen |
| LONG64 | l64indgen |
| ULONG64 | ul64indgen |
| FLOAT | findgen |
| DOUBLE | dindgen |

例えば：
```idlang
print, findgen(4,3)
```
```:結果
      0.00000      1.00000      2.00000      3.00000
      4.00000      5.00000      6.00000      7.00000
      8.00000      9.00000      10.0000      11.0000
```

値が並ぶINTの一次元の配列なら`[ ]`と`:`でこうやって作ることもできます。
```idlang
arr1 = [2:6]
print, arr1
arr2 = [3:7:2]
print, arr2
```
```:結果
       2       3       4       5       6
       3       5       7
```


ある値だらけの配列が欲しい場合`replicate`を使います。
```idlang
print, replicate(1.5, 4, 3)
```
```:結果
      1.50000      1.50000      1.50000      1.50000
      1.50000      1.50000      1.50000      1.50000
      1.50000      1.50000      1.50000      1.50000
```

ランダム値で配列を生成することもできます。例えば`randomu`は0~1の値を一様分布でランダムします
```idlang
print, randomu(0, 4, 3)
```
```:結果
    0.0305810     0.213140     0.299003     0.381139
     0.863488     0.133443    0.0734653     0.924736
     0.642876     0.135658     0.614181     0.758658
```

ここでは1つ目の引数はseedの番号で、次からの引数はサイズ。

正規分布ランダム値が欲しい場合`randomn`を使います。
```idlang
print, randomn(0, 5, 2)
```
```:結果
    -0.888299    -0.921606      1.38146     -1.41349    -0.383255
    -0.432699      1.26306      1.17744      1.20992      1.76123
```


## 配列の再構成に使う関数

| 関数 | 意味 |
|:-:|:-:|
| reform | 指定の形に変形する |
| transpose | 転置行列 |
| reverse | 配列の順番を逆にする |
| rotate | 配列を回転する |
| shift | 配列の要素をシフトする |
| sort | 配列の要素の値の順番を返す |

`sort`関数はただ値の順番を返すだけで、本当に並べ替えるためにはその結果をインデックスに使う必要があります。それを逆にしたいなら`reverse`とも使います。
```idlang
arr = [4,2,0,6,3]
print, sort(arr)
print, arr[sort(arr)]
print, reverse(arr[sort(arr)])
```
```:結果
           2           1           4           0           3
       0       2       3       4       6
       6       4       3       2       0
```


## 統計関数

| 関数 | 意味 |
|:-:|:-:|
| min | 最小値 |
| max | 最大値 |
| total | 総計 |
| mean | 平均値 |
| median | 中央値 |
| stddev | 標準偏差 |
| variance | 分散 |

どれも二次元以上の配列に使う時に`dimension`というキーワードで処理する軸だけの計算を指定することができます。

例：
```idlang
arr = [[1,2,3],$
       [4,5,6],$
       [7,8,9]]
print, mean(arr, dimension=2)
```
```:結果
      4.00000      5.00000      6.00000
```

`dimension=0`か指定しない場合は全体を考慮することになって一つだけの数字を返します。


## where関数

`where`はIDLでよく使われて汎用性が高い関数です。

一番基本な使い方は、条件に合う要素のインデックスを取ること。これを使って指定の条件の要素だけ取ることができます。
```idlang
arr = [1,8,11,3,14]
print, where(arr gt 7)
print, arr[where(arr gt 7)]
```
```:結果
           1           2           4
       8      11      14
```

2番目の引数に変数を入れたらその変数はできたインデックスの数を持つ。
```idlang
arr = [6,7,7,7,8]
print, where(arr eq 7, n_ok)
print, n_ok
```
```:結果
           1           2           3
           3
```

`complement`キーワードに変数を入れたら残りの（つまりその変数が条件に反する）インデックスを持つ。
```idlang
arr = [2,3,3,2,2,1]
print, where(arr eq 2, complement=com)
print, com
```
```:結果
           0           3           4
           1           2           5
```

どの要素も条件に合わない場合は`-1`が返ってくる。ただし`/null`キーワードを入れたら代わりに`!NULL`が返ってくる。
```idlang
arr = [1,2,4]
print, where(arr gt 5)
print, where(arr gt 5, /null)
```
```:結果
          -1
!NULL
```

`where`の結果をインデックスに使う場合`/null`を使った方がいいかもしれません。`-1`だと最後の要素を取ることになるから、これは望まない結果になるかもしれない。`!NULL`をインデックスする場合取れる値も`!NULL`となります。
```idlang
arr = [2,3,-2,-3,4]
print, arr[where(arr eq 0)]
print, arr[where(arr eq 0, /null)]
```
```:結果
       4
!NULL
```

`where`を使って特定の条件の要素だけ書き換えることができます。
```idlang
arr = [1,2,3,4,5,6]
arr[where(arr gt 3)] = 3
print, arr
```
```:結果
       1       2       3       3       3       3
```

この方法は二次元以上の行列にも通用するから便利です。
```idlang
arr = [[-2,1,-3],$
       [3,4,-7],$
       [-1,0,4]]
arr[where(arr lt 0)] = 0
print, arr
```
```:結果
       0       1       0
       3       4       0
       0       0       4
```


# 構造体
IDLはJavaScriptのオブジェクトみたいに構造体を作ることができます。
```idlang
hito = {namae: 'tarou',$
        nenrei: 25,$
        shinchou: 166.6}
print, hito
```
```:結果
{ tarou      25      166.600}
```

`create_struct`関数で作ることもできますが、ちょっとわかりにくいかも。
```idlang
hito = create_struct('namae', 'tarou',$
                     'nenrei', 25,$
                     'shinchou', 166.6)
```

要素へのアクセスは`.`を付けるのです。普通の変数みたいに書き換えることもできます。
```idlang
print, hito.namae
hito.namae = 'momotarou'
print, hito.namae
print, hito.nenrei
```
```:結果
tarou
momotarou
      25
```

ただしJavaScriptみたいに最初から存在しない要素は入れられません。
```idlang
hito.taijuu = 60.
```
```:結果
% Tag name: TAIJUU is undefined for STRUCT.
```

構造体に`help`を使うと中身の詳細まで読めます。

```idlang
help, hito
```
```:結果
** Structure <Anonymous>, 3 tags,memsize =40, data length=11/11:
   NAMAE           STRING    'tarou'
   NENREI          INT             25
   SHINCHOU        FLOAT           166.600
```


# 条件分岐
if else while forなど。

## if else文

IDLの`if`文はこう書きます。
```idlang
if 条件 then begin
    条件を満たしたら実行する内容
endif
```

更に`if` `else`文はこう書きます。
```idlang
if 条件1 then begin
    条件1を満たしたら実行する内容
endif else if 条件2 then begin
    条件2を満たしたら実行する内容
...
...
endif else if 条件n then begin
    条件nを満たしたら実行する内容
endif else begin
    どの条件も満たさない場合実行する内容
endelse
```

ちょっと冗長でわかりにくい気もします。

例：
```idlang
if a gt b then begin
    print, 'a wa ookii'
endif else if a lt b then begin
    print, 'b wa ookii'
endif else begin
    print, 'gobugobu'
endelse
```

ただし一行で書ける場合`begin`と`endif`と`endelse`は省略できます。
```idlang
if 条件 then 真の場合の内容 else 偽の場合の内容
```

例：
```idlang
if nedan gt 10000 then print, 'takai' else print, 'yasui'
```


## case文

条件を分けるために`if` `else`の他にも`case`文があります。

使い方：
```idlang
case 変数 of
    値1: begin
        変数は値1なら実行したい内容
    end
    値2: begin
        変数は値1なら実行したい内容
    end
    ...
    ...
    値n: begin
        変数は値1なら実行したい内容
    end
    else: begin
        変数はどの値でもないなら実行したい内容
    end
endcase
```

ただし内容が1行で書ける場合、`begin`と`end`は省略できます。

```idlang
case 変数 of
    値1: 変数は値1なら実行したい内容
    値2: 変数は値1なら実行したい内容
    ...
    ...
    値n: 変数は値1なら実行したい内容
    else: 変数はどの値でもないなら実行したい内容
endcase
```

## while do文
ループをするために`while` `do`文を使います。

書き方：
```idlang
while 条件 do begin
    実行したいないよう
endwhile
```

例：
```idlang
i = 3
while i lt 10 do begin
    print, i
    i += 2
endwhile
```
```:結果
       3
       5
       7
       9
```

ただし一行で書ける内容なら　`begin`と`endwhile`は省略できます。

```idlang
while 条件 do 実行したいないよう
```

例：
```idlang
i = 5
while i lt 8 do print, i++
```
```:結果
       5
       6
       7
```

## repeat until文

`repeat` `until`文は`while` `do`と似ていますが、条件は最後に書くのです。
```idlang
repeat begin
    実行したい内容
endrep until 止める条件
```

例：
```idlang
i = 0
repeat begin
    i -= 3
    print, i
endrep until i lt -7
```
```:結果
      -3
      -6
      -9
```

## for do文

```idlang
for 変数=初期値, 最終値, 各ループで変わる値 do begin
    実行する内容
endfor
```

各ループで変わる値が`1`である場合は省略できます。

例：
```idlang
a = 0
for i=1, 4 do begin
    a += i
    print, a
endfor
```
```:結果
       1
       3
       6
      10
```

一行で纏められる場合`begin`と`endfor`は省略できます。

```idlang
for 変数=初期値, 最終値, 各ループで変わる値 do 実行する内容
```

例：
```idlang
for i=9, 4, -2 do print, i
```
```:結果
       9
       7
       5
```

## foreach文

`foreach`文は配列の要素を一つずつ取ってループするのです。

書き方：
```idlang
foreach 要素を取る変数, 配列 do begin
    実行する内容
endforeach
```

一行の場合の省略。
```idlang
foreach 要素を取る変数, 配列 do 実行する内容
```

例：
```idlang
arr = [2,5,8]
s = 0
foreach x, arr do begin
    s += x
    print, 'x = ', x
endforeach
print, 's = ', s
```
```:結果
x =        2
x =        5
x =        8
s =       15
```


## break

ループの中で途中で脱出するために`break`を使います。

例：
```idlang
i = 2
while 1 do begin
    i *= 2
    print, i
    if (i gt 9) then break
endwhile
```
```:結果
       4
       8
      16
```


## continue

`continue`も`break`と同様な使い方だが、脱出ではなくすぐ次のループへスキップするために使うものです。

```idlang
for i=1,4 do begin
    if (i eq 3) then continue
    print, i
endfor
```
```:結果
       1
       2
       4
```


## goto文

一応古い言語なので`goto`文も存在するけど、現在あまりおすすめしないものなので、割愛します。


# ファイルの読み書き

IDLではこれらのプロシージャでファイルを読み書きできます。
| 関数 | 役目 |
|:-:|:-:|
| openr | 既存ファイルを読むために開く |
| openw | 新しいファイルを書くために作る |
| openu | 既存ファイルを読み書きするために開く |
| readf | 開いているファイルを読んで変数に収める |
| printf | 開いているファイルに書く |
| close | 開いているファイルを閉じる |

例えば新しいファイルを作ってその中に何かを書く場合はこのようにします。
```idlang
openw, 1, 'kaite.txt'
printf, 1, 'omae wa mou kaite iru'
close, 1
```

まずは`openw`ファイルを開いて、最後に`close`でファイル。ファイルを閉じなければ出力された文字はまだ反映されません。

ここで数字`1`は処理するファイルを示す任意の数字であり、全て関連するプロシージャで同じ数字を使います。同時に他のファイルを扱うなら他に数字を付けます。

読み込む時に`openr`で開いて`readf`で読みます。
```idlang
s = ''
openr, 1, 'kaite.txt'
readf, 1, s
print, s
close, 1
```
```:結果
omae wa mou kaite iru
```

ここで`s`は読み込んだ内容を受け取る変数です。ただし注意すべきは、予め変数を文字列型に宣言しておかないとFLOAT型になってしまうこと。つまり適当な文字列を代入すること。ここでは空の文字列`''`。

逆に言うと、もし読みたいものはFLOATなら予め変数を準備しなくてもいいし、勝手にFLOAT型になる。例えばこんなファイル：
```doraefloat.txt
129.3
```
読み込んでみる。
```idlang
openr, 1, 'doraefloat.txt'
readf, 1, f
help, f
close, 1
```
```:結果
F               FLOAT     =       129.300
```

`readf`は1行ずつ読むから全部読むのに`while` `do`文を使うことができます。例えばこんなファイル：
```hogefloat.txt
1.111
11.11
111.1
```
実行する。
```idlang
openr, 1, 'hogefloat.txt'
while ~eof(1) do begin
    readf, 1, f
    print, f
endwhile
close, 1
```
```:結果
      1.11100
      11.1100
      111.100
```

ここで`eof`はそのファイルは終わったかどうかをチェックする関数です。


# グラフィック機能

IDLはグラフィック機能も持ってグラフを描くことなど可視化することができます。3Dもできます。

例えばグラフを描きたい時こう使います。
```idlang
x = findgen(1000)/50
y = cos(x)
plot, x, y
```

そしてこんなグラフができます。地味に見えますが、色々調整して綺麗にすることもできます。

![gdlgraph.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202903/7c0d4d27-41b5-88d0-04bc-92ec10b5621e.png)

ただし無料のGDLではグラフィック機能が限られるから、この部分は本物のIDLを買わないと色々できません。

その他に詳しいことは割愛しますが、本当にもっと色々できます。


# 参考

日本語
- [IDL 初〜中級者のための天文データ解析用](https://www.adc.nao.ac.jp/J/cc/public/koshu_shiryo/2018/IDL/IDL_lecture_18Nov.pdf)
- [天文データセンター 講習会資料](https://www.adc.nao.ac.jp/J/cc/public/koshu_shiryo.html#idl_fits)
- [西田圭佑のIDLメモ](https://www.kwasan.kyoto-u.ac.jp/~nishida/idl/)
- [IDL | 国内総販売元](https://www.nv5geospatialsoftware.co.jp/Software-Technology/IDL)
- [吉川裕のIDLに関する備忘録集](https://www-ocea.kugi.kyoto-u.ac.jp/yosikawa/Tips/IDL/)

他のqiitaの記事
- [IDL(Interactive Data Language)で初心者が気をつけるべきポイント](https://qiita.com/kemekun_gmc/items/0d6f1ef43eeb961025a7)
- [IDLでテキストファイルを読み込み、配列に格納する方法](https://qiita.com/FujiKoji/items/d36eb1150eb4f01aa3ed)

英語
- [IDL Reference](https://www.nv5geospatialsoftware.com/docs/routines-1.html)
- [Getting started with GDL](http://www.sgeier.net/tools/GDL-intro.pdf)
- [IDL Course: Contents - SIE de Investigaci&oacute;n y Ense&ntilde;anza](https://research.iac.es/sieinvens/SINFIN/CursoIDL/cidl.php)

その他に、自分は買っていませんが、一応よくできた日本語の書籍もあります。
- [ＩＤＬプログラミング入門―基本概念から３次元グラフィックス作成まで― Kindle版](https://www.amazon.co.jp/dp/B07L2W3LQ4)
- [IDLと3次元画像処理入門―パソコンで作れる鳥瞰図](https://www.amazon.co.jp/dp/4320121910)


# 終わりに

以上はIDLとGDLというプログラミング言語の紹介と入門でした。実際に使う人はあまりいないかもしれませんが、プログラマーとして新たしい言語を知って視野を広げることになもなりますし。

これは自分が忘れないためのメモでもありますが、必要な誰かのために役に立てば幸いです。