Top/マニュアル/文法/5.配列
  トップページへ   [ 一覧 | 検索 | 最終更新 ]   [ 差分 | 履歴 | 凍結 ]

配列

演算子[ ]は配列要素にアクセスするための演算子です。 配列には文字列スプリットを擬似的に配列と見なす「簡易配列」と、カンマ区切りで列挙された要素を扱う「汎用配列」の2種類があります。

簡易配列

文字列に含まれるカンマをデリミタ(区切り文字)と解釈して、配列のように扱う機能です。

request
{
    _a = "this,is,a,pen"
    _a[1]
}

requestは"is"を出力します。 [ ]演算子が処理する対象は変数である必要はありません。即値でも関数の返値でもいいです。
上の例は以下のように書いても同じです。

request
{
    "this,is,a,pen"[1]
}

序数の後ろにカンマ区切りでデリミタを指定することにより、カンマでなく他の文字列で簡易配列要素を区切ることができます。

request
{
    "This is a island."[2,"is"]
}

"is"で区切るわけですから、文字列は以下のように分解されます。

[0] "Th"~
[1] " "~
[2] " a "~
[3] "land."~

したがってrequestは" a "を返します。 デリミタ指定をうまく使うと、多次元配列風に値を取り出すことが出来ます。

request
{
    _ar = "taro|male,ayame|female,hotaru|female"
	
    _ar[2][1,"|"]
}

_ar[2]は"hotaru|female"です。さらに"|"を区切り文字として解釈して[1]を取り出しますので、結果は"female"となります。 このように、階層毎にユニークなデリミタを適用することによって、任意の位置の値を簡単に抜き出すことができるようになります。 範囲外の序数を指定した場合の出力は、存在しない変数の値を取り出そうとした場合と同様、空の文字列となります。

簡易配列と汎用配列を利用した多次元配列

もっと直接的に多次元配列を扱いたいなら

Food = "うどん,ラーメン,おにぎり"
Color = "赤,青,黄"

と簡易配列を宣言し、汎用配列で

Word = (Food, Color)

とすれば Word[0][0]でうどん、Word[1][2]で黄となる

___________________________________________

ここからは変数限定の機能を解説します。 要素に代入が可能です。

request
{
    _a = "this,is,a,pen"
    _a[3] = "eraser"
    _a
}

"pen"を"eraser"に書き換えています。requestの実行結果は"this,is,a,eraser"となります。 デリミタ指定しても正しく機能します。

request
{
    _s = "This is a island."
    _s[2,"is"] = " beautiful "
    _s
}

requestは"This is beautiful island."を出力します。 多次元配列風に[ ]演算子を連結して使用している場合、代入はできません。

request
{
    _ar = "taro|male,ayame|female,hotaru|female"
	
    _ar[1][1,"|"] = "male"
}

ayameの性別をmaleに書き換えようとしていますが、この操作はエラーとなります。代入が可能なのは一次元の場合のみです。 現在の要素数を越える位置へも問題なく代入できます。デリミタが自動的に追加され、要素数が拡張されます。

request
{
    _m = "fuji/asama/tanigawa"
    _m[5,"/"] = "daisen"
    _m
}

requestは"fuji/asama/tanigawa///daisen"を出力します。 SETDELIMという関数を使用すると、「デフォルトのデリミタ」をカンマから別の文字列へ変更できます。
先に示した例をSETDELIMを使用して書き換えたものを以下に示します。

request
{
    _m = "fuji/asama/tanigawa"
    SETDELIM(_m, "/")
    _m[5] = "daisen"
    _m
}

SETDELIMすることにより、単純に_m[5]と書くことができるようになります。 多次元配列風に[ ]演算子を連結して使用している場合、SETDELIMは最初の(一次元目の)[ ]のみで有効です。

汎用配列

汎用配列はさまざまな型の値を混在して格納できる配列構造です。
一般的なアクセスでは簡易配列より高速に動作します。

初期化

i = (100,"test",-1.5)

要素をカンマで列挙して記述します。
代入する場合は上のように要素の集合を( )で囲んでください。カンマの演算優先度は代入よりも低いため、ブラケットが無いと

(i = 100),"test",-1.5

このように解釈されてしまいます。 配列を空の状態で初期化するには、IARRAYという関数を使用します。IARRAYは「空の汎用配列」を返す関数です。

i = IARRAY

初期化時に要素をひとつだけ代入したい場合は工夫が要ります。たとえば単に i = 100 としたのでは、配列ではなくただの数値の代入になってしまうからです。
以下のように記述します。

i = (IARRAY,100)

要素追加

i = (i,"add")

とすると配列の後端に"add"が追加されます。 配列を追加することもできます。

i = (i,("add",123,0.0))

a = a + 1 を a += 1と略せるように、上の例は下のようにも書くことができます。

i ,= ("add",123,0.0)

先頭への挿入も同じ書式で可能です。

i = ("first",i)

中途への挿入もできます。

i = (100,200,300,400,500,600)
i[2] ,= "insertion"

iは(100,200,300,"insertion",400,500,600)となります。
挿入対象は[2]でなく[3]に入ることに注意してください。 i[2]へ挿入したい場合は

i[2] = ("insertion",i[2])

とします。

要素削除

削除したい要素へIARRAYを代入します。

i = (100,200,300,400,500,600)
i[2] = IARRAY

300が削除され、iは(100,200,400,500,600)となります。

値の更新

単純に要素へ代入できます。 現在の要素数を越える位置へも問題なく代入できます。必要な数だけ要素数が拡張されます。

値の取り出し

通常の変数と同様、序数を指定して記述すればそのまま出力されます。 範囲外の序数を指定した場合の出力は、存在しない変数の値を取り出そうとした場合と同様、空の文字列となります。

i = (100,200,300,400,500,600)
i[4]

500が出力されます。 [ ]演算子が処理する対象は変数である必要はありません。即値でも関数の返値でもいいです。

(100,200,300,400,500,600)[4]

500が出力されます。 汎用配列をそのまま関数の出力にできます。

request
{
    river[2]
}

river
{
    ("tenryu","bandou-tarou","ishikari","shimanto")
}

requestは"ishikari"を出力します。

多次元化はできない

汎用配列は多次元配列を構成できません。

(100,200,(300,400),500,600)

このように書いても、内包されたブラケット部分が副次的な配列と認識されることはありません。
結局以下のように単純に結合されてしまいます。

(100,200,300,400,500,600)

演算

要素単位の演算は通常に行うことが出来ます。

汎用配列と単項値との演算は、「全要素へ単項値が演算される」という独特の動作となります。

pref = ("gunnma","ohsaka","hokkaido")
    pref += "-ken"

prefは "gunnma-ken","ohsaka-ken","hokkaido-ken" となります。

answer = (2*(1,2,3))[1]

answerには4が代入されます。
2*(1,2,3)の計算結果が(2,4,6)となるからです。

汎用配列どうしの演算は、演算可能なすべての組み合わせを出力します。

pref1 = ("gunma","osaka","hokkai")
    pref2 = ("-ken","-fu","-do")
    pref3 = pref1 + pref2

pref3は "gunma-ken","gunma-fu","gunma-do","osaka-ken","osaka-fu","osaka-do","hokkai-ken","hokkai-fu","hokkai-do" になります。
配列に数値(整数、実数)が入っている場合は、引き算・割り算・掛け算など他の演算も有効です。

関数の引数

文において関数の引数は汎用配列です。_argvの内容は、呼び出し側引数がそのまま代入された汎用配列となっています。 つまり

func(1, 2, "test")

という関数の呼び出しは、

_i = (1,2,"test")
func(_i)

とも書けます。非常にトリッキーな表記ですが、これで意図どおりに動作します。
注意してください。書き直したスクリプトにおいても、引数の数は決して1個ではありません。3個です! この構造をうまく利用すると、可変長の引数を他の関数へ簡単に渡すことが出来ます。

request
{
    total(1,2,3,4,5,6)
}

total
{
    calc_total(_argv)
}

calc_total
{
    _answer = 0;
    foreach _argv; _val { _answer += _val }
    _answer
}

totalは自分では何もせず、すべての引数をそのままcalc_totalへ引き渡しています。
この例では単純に引き渡していますが、もちろん必要に応じて加工してから渡すことも可能です。 引数の指定方法が複雑になっている場合は注意すべきです。

_i = (1,2,"test")
func("sky", _i, "sun")

上の呼び出しは以下と等価です。汎用配列は多次元化できないことを思い出してください。

func("sky", 1, 2, "test", "sun")

デリミタ/取得数指定

_i = (2,"is")
"This is a island."[_i]

簡易配列のデリミタ指定の部分も汎用配列です。したがって上のようなことも出来ます。これは下の記述と等価です。

"This is a island."[2,"is"]

範囲指定

簡易配列/汎用配列とも、序数を範囲で指定できます。取得、代入とも可能です。 範囲は汎用配列で指定します。たとえば i[a,b] は「iの要素a~b」を表します。

name = ("さくら","せりこ","奈留","まゆら","毒子","美耳")
i = name[1,3]
name[3,4] = "奎子"
j = name
name[0,2] = IARRAY
k = name

iには ("せりこ","奈留","まゆら") が格納されます。
jは ("さくら","せりこ","奈留","奎子","美耳") 、
kは ("奎子","美耳") となります。

範囲外は無視されます。

n = (1,2,3,4)
n[-2,1] *= 5

nは (5,10,3,4) です。 対象が簡易配列の場合でも書式は同じです。

name = "さくら,せりこ,奈留,まゆら,毒子,美耳"
i = name[1,3]
name[3,4] = "奎子"
j = name

iは "せりこ,奈留,まゆら" 、jは "さくら,せりこ,奈留,奎子,美耳" です。 デリミタ指定も可能です。範囲指定のすぐ後ろに続けて指定します。

animal = "くま!うさぎ!ねこ!いぬ!わに"
i = animal[0,2,"!"]
animal[2,4,"!"] = "ぶた"
j = animal

iは "くま!うさぎ!ねこ" 、jは "くま!うさぎ!ぶた" です。

汎用配列のパラレル出力

すべての式/値の前に「parallel」を書くことができます。
parallelは出力候補値が汎用配列だった場合、 要素のそれぞれを出力候補値にする機能を持っています。

foo0
{
  ("A","B","C")
  "地球"
}

foo1
{
  parallel ("A","B","C")
  "地球"
}

foo0の出力は、("A", "B", "C") もしくは "地球"。
foo1の出力は、"A"、"B"、"C"、"地球" のいずれかとなります。 汎用配列以外にparallelを使っても意味がなく、書かないのと同じです。たとえば下の2つの記述は等価です。

parallel STRLEN("earth")
STRLEN("earth")

択一メソッドarrayとparallelを利用することにより、関数の出力候補と汎用配列を相互に変換することができます。
さまざまな応用が考えられます。たとえば以下の非常に簡潔な関数cyclicは、汎用配列から記述順に要素値を取り出します。

cyclic : sequential
{
  parallel _argv
}

逆に、すべての式/値の前に「void」と書くことで、計算結果を破棄し、出力候補にしないこともできます。