Category:LSL List/ja

From Second Life Wiki
Jump to navigation Jump to search

リストはゼロ個以上の要素を扱える特殊なデータ型です。

リストはその要素を角括弧で囲んで表記します。各要素はカンマで区切られます。

例:

[0,1,2,3,4]

["Yes","No","Perhaps"]

(注意: 他のプログラミング言語の経験者向けに付け加えると、LSL には配列は無く、リストだけがあります。)

様々なデータ型

必ずしもリストの全要素が同じデータ型である必要はありません。あるリストの中に、strings 型、integer 型、float 型、vector 型などの要素を並存させる事ができます。

例: //integer 型、float 型、string 型、vector 型の要素を含んだリスト

[1,14.154,"Isn't this fun?",<0,0,0>]

しかし、リストを別のリストの要素とすることはできません。(すなわち、リストの入れ子はできません。)

[1, "one", 2, "two"] + [3, "three"] の結果は [1, "one", 2, "two", 3, "three"] であり、
[1, "one", 2, "two", [3, "three"]] ではありません。

リストに要素を追加する際、追加要素のデータ型をリストは自動的に記憶します。

通常、あなたはリストに要素を追加した当人なのですから、リストのどこにどんな型のデータがあるか知っているはずです。そしてそれを適切な List2<type> 関数、すなわち llList2StringllList2Vector など (後述) を使いリストから取り出せます。

しかし、もし何らかの理由でリスト要素のデータ型を確認したい場合は、llGetListEntryType 関数を使えます。

注意: リストに float 型のデータを加える場合、型を確実に保持するためにも、小数点付き (例えば 1 でなく 1.0) にしてください。

リストは string 型へ直接、型キャストできます。

default
{
     touch_start(integer total_number)
    {   
        list a = ["abc",1,2,3.14,<0,0,0>];
        llOwnerSay((string)a); // outcom:  abc123.140000<0.000000, 0.000000, 0.000000>
    }
}
     integer min; integer max; integer total;
     llSay(0, (string) ["Minimum: ", min, "  Maximum: ", max, "  Total: ", total] );  // requires 1 explicit cast instead of 3

または do while/ja を使うことによって分けて発言することができます

default
{
     touch_start(integer total_number)
    {   
        list MyList = ["abc", "def", "ghi", "jkl", "lmn", "opq"];
        integer i;
        integer length = llGetListLength(MyList);
        do
            llOwnerSay(llList2String(MyList, i) );
        while(++i < length);
    }
}

一般的なリスト操作

リスト内の位置と、リストの長さの数え方

まず最初に、以下の点に注意するのが大切です。(経験者でも疲れていると躓くポイントです。)

["Yes","No","Perhaps"]

リストの要素は 3 個なので、リストの長さは 3 です。リストの長さは llGetListLength 関数で返されます。

integer length = llGetListLength(mylist);

しかし、リスト内の要素の位置 (いわゆる "インデックス") は 0 から数え始めます。-- 1 ではありません。

上の例の "Yes" の位置は 0、"No" は 1、"Perhaps" は 2 です。

従って、リストに要素が 7 個あったとしたら、最後の要素の位置は 6 です。

リスト内の位置は、負のインデックスを使用して右端から左に進むことによってもカウントできます。

したがって、右端のエントリの位置は-1であり、左端のエントリの位置はリストの長さの負数です。

したがって、リストの前方の位置やリストの長さを事前に知る必要なく、リストの末尾に近い要素を取得するには、次のように記述することができます

integer length = llGetListLength(mylist);
string item = llList2String(myList,length - 1);

string LastItem   = llList2String(myList, -1);
string LastButOne = llList2String(mylist, -2);   // etc.

リストの制限

スクリプトが実行されている間、リストは必要に応じて動的に大きくなりえます。そのサイズは、スクリプトが利用可能なメモリの量で決まります。

ただしコンパイル時 (保存時) には、スクリプトにハード コードされた定義済リストは最大 72 個の要素しか持てないという制限があります。例えば多くのカラー バリエーションを用意するといった場合などは、こうした非常に長い定義済みリストがあり得ます。

注意: 定義済みリストとしてどうしても 72 個を超える要素を使いたいという場合は、コンパイラが対応できるよう 2 個以上のリストに分割しておき、state_entry イベントなり適切なタイミングでそれを結合してください。

biggerlist = biglist01 + biglist02;

リストへの要素の追加

既存のリストに要素を追加する方法は、前置/後置を使用して複数あります。:

  1. myList = [new_item] + myList;   //  Mono-LSLで前置するための最良の方法。注:新しいアイテムはリストとして提供されます
    
  2. myList = myList + new_item;     // Monoで単一の新しいエントリを後置するための最良の方法。新しいアイテムはリストとして提供されません
    
  3. myList += new_item;             // 簡略化された構文でアイテムを後置
    
  4. myList = myList + [new1, new2]; // リストを後置して連結(単一の新しいアイテムの場合、Monoでは効率が低い)
    
  5. myList += [new1, new2];         // 簡略化された構文でリストを後置して連結
    
  6. myList = (myList=[]) + myList + [new_item]; // LSO向けに推奨。メモリの断片化を最適化した後置のリスト連結
    
  7. myList = (myList=[]) + myList + new_item;   // LSO-LSL向けに推奨:メモリの断片化を最適化した後置のアイテム
    


注意

  • メソッド1は、Mono-LSLの場合に最もメモリ効率が高いですが、LSOの場合はそうではありません。
    • メモリの節約を考慮せずに新しいアイテムを前置する場合、メソッド1はMono-LSLでもメモリの節約が無効になります。
    • メソッド1は、LSO-LSLの場合、他のメソッドよりも多くのメモリを消費します。
  • 2009年8月8日現在、メソッド7がLSO-LSLで最もメモリを節約できる方法です。
  • メソッド2および3は同じものにコンパイルされます。
  • メソッド4および5は同じものにコンパイルされます。
  • メソッド6および7は、LSO-LSLでの他の方法に比べてかなりのメモリ節約をもたらすことがありますが、LSO-LSL VMのバグにより、文字列とキーの型変換が正しく動作しないことがあります:SVC-1710(それにより、使用不能なヒープメモリのブロックが発生しないようにヒープの断片化が減少します)[1]。Mono-LSLでは、特にメモリの利点やデメリットはありません。
    • 状況に応じて(LSO-LSLの場合)、この方法は何の利点も提供しないことがあります。疑念がある場合は、この方法を使用せずにスクリプトのプロファイルを作成してください。

リストの結合

リストは + 記号で単純に結合できます。

newlist = list01 + list02;

newlist = list01 + ["red","brown",<0,0,0>];

注意:上記の例は、コマンドが実行される間に実際には3つのリストをメモリ内に作成しますが、返されるのは1つだけです。これはメモリ使用量に影響を与える可能性があります。

注意:LSLでは、連結は実行可能なコードでのみ行うことができます。グローバルなリストを宣言する際に+を使用することはできません。

リストを空にする

リストを空にする場合は、以下のように空の角括弧を等号で代入してください:

myList = [];

リストを関数に渡す

リストを関数に渡すことは、他のデータ型を渡すのとほぼ同じですが、いくつかの有用なケースに注意する必要があります:

myList = llListReplaceList(myList, ["myString"], 2, 2);

上記のコードでは、llListReplaceList/ja() を呼び出しています。これは比較的単純な操作のように見えますが、リストの渡し方や llListReplaceList/ja()、llDeleteSubList/ja()、 llList2List/ja()、llListSort/ja()(およびその他の関数)などの関数が動作する方法によって、その関数を呼び出すことで、必要なリストを保存するのに必要なメモリ量の2倍、3倍、または4倍のメモリを使用することがあります!この問題を回避するために、最適化の小さなテクニックを使用できます。関数に渡すリストが二度と読まれないことがわかっている場合(たとえば、関数の結果がリストを上書きする場合)、次のように書けます

myList = llListReplaceList((myList = []) + myList, ["myString"], 2, 2);

これによって、LSO-LSLのメモリ使用量と断片化が大幅に削減されますが、Mono-LSLでは影響がありません(Monoでは断片化はスクリプトに影響を与えません)。このテクニックは時々「Voodoo magic」と呼ばれることもあります。関数呼び出し以外の場合でも(上記のようにリストを連結する場合など)、これによってメモリの問題がほぼ解消されることがあるかもしれません:

list list1 = ["a", "b", "c"];
list2 = ["d", "e", "f"];
list3 = (list1 = list2 = []) + list1 + list2;

リストを別のリストに処理する

より複雑なケースですが、大きなリストを処理していると、結果として同じように大きなリストが生成されることがあります。このような場合、メモリが枯渇するリスクが非常に高くなります。したがって、特に大きなリストで作業を行うことになる場合、それらを操作する価値がある場合があります:

list myOutput = [];

integer i = 0; integer x = myList != [];
for (; i < x; ++i) {
    if (i > 10) { // 10個の要素ごとにリストを削除
        myList = llDeleteSubList((myList = []) + myList, 0, i - 1);
        x -= i;
        i = 0;
   }

   // ここで作業を行う:
   myOutput += llList2List(myList, i, i); // サンプルの作業
}

この方法(数個のリストエントリを削除またはストライドする)は、ループごとにエントリを削除するよりも望ましいです。なぜなら、llDeleteSubList/ja() を呼び出すコストが非常に高いからです。リストの入力を整理するための最適なチャンクサイズを決定するのは、メモリ使用量と削除コストのバランスを取る必要があるので、スクリプターの判断によります。

リストの比較

リストの等価テストは内容を比較するのではなく、長さのみを比較します。したがって、次のようなコード:

    if ( [1,2,3,4] == [0,0,0,0] )
    {
        // TRUE path
    }
    else
    {
        // FALSE path
    }

は結果はTRUEになります.

一方、!= 演算子は、比較がTRUEまたはFALSEしか返さないLSLのルールを破り、両方のリストの長さの差を返します。したがって、以下の2つの文は等価であり、ab はどちらもこのコードを実行した後で -2 に等しくなります:

    integer a = [1,2] != [3,4,5,6];
    integer b = llGetListLength([1,2]) - llGetListLength([3,4,5,6]);

リストの内容を含めて実際に等しいかどうかを比較するには、ListCompareを参照してください。

飛び石リスト (ストライド リスト)

リストの一般的な使われ方のひとつに、データの構造的な集積 (いわゆる構造体) の機能を模倣するという点があります。構造体は、多くのプログラミング言語で使用可能ですが、LSL では扱えません。

(2021年7月時点でも)SL で、ある種の構造と共にある程度の量のデータを保存するのに使える最もよい手段が、データの扱いに制約は多いですが、飛び石リスト (ストライド リスト) です。

飛び石リストには、それぞれグループ化 (ストライド化) された、関連データ構成を保存できます。

ここでは例で示すのが良いでしょう。アバターの集団について、各人の名前、性別、誕生日を記録するのに飛び石リストを使えます:

list demographics = ["John Adams", "male", "2007-06-22", "Shirley Bassey", "female", "2005-11-02", "Matt Damon", "male", "2008-05-19"];

この例は、各グループ (ストライド) が 3 個のデータ要素 (名前・性別・誕生日) を持つので、3 要素からなるストライドを持つといえます。0 番目のデータ ("John Adams") は最初のグループの先頭で、3 番目のデータ ("Shirley Bassey") は二番目のグループの先頭、といった具合です。

重要なのは、各グループの各要素は、リストの中で常に同じ順番を保たなければいけないという事です。上の例ならば、三要素のうち名前は常に先頭になければいけません。リストをソートする llListSort 関数はグループの先頭要素しかソートしませんので、記録する情報の順番を注意深く考慮すべきです。言い換えると、アバターの誕生日がスクリプトの処理で最も重要な属性という場合、それを先頭に持ってきて、名前を二番目にする必要があるでしょう。アバターをファースト ネームでなくラスト ネームで並び替えられるようにしたい場合、LastName FirstName という形式で名前をリストに追加すべきです。(カンマが無いのは、ラスト ネームとファースト ネームを別要素にしないという例だからです。)

上のリストに別の人を追加するには、以下のようにします:

demographics += ["Dorthy Lamour", "female", "2010-01-22"];

非常に大きなストライドリストを操作する場合、リストを編集することを期待している場合、各「列」ごとに1つのリストを使用することを検討することがあるかもしれません。これにより、リストを操作する際の必要なメモリ量が大幅に削減されますが、ソートする際にはかなり難しくなります。

SL 以外のデータベースや表計算ソフトで使えるような、スマートなデータ分析やデータ操作を、飛び石リストでは何ら使えません。しかし外部の補助ツールが使えない状況ならば、SL でのある種の限定的な用途には合うでしょう。

飛び石リストを扱えるツールを紹介します:


(2021年7月時点) 以下の 3 つの組み込み LSL 関数が飛び石リストを扱えます:

注意して使用すれば、llListStatistics/ja 関数は整数や浮動小数点数以外のアイテムを無視してストライドリストに使用できます。

以下はユーザが作成し追加した、飛び石リスト用の関数です。

関数 説明
ListStridedMove 飛び石リストのある要素を、リスト内の別の場所へ移動します。
ListStridedRemove 飛び石リストの一部を削除します。
ListStridedUpdate 飛び石リストの一部を更新します。
ListStridedReorder 各ストライドの内容を再配置するか、ストライド全体を再配置できます。

応用的なリスト操作

以下は、組み込み LSL 関数では対応できない処理を行なうため LSL ユーザによって作成、提供された関数です。


関数 説明
ListCast 要素の型を統一するようリストを処理します。
List_cast リストの内容を処理し、それらの内容を文字列からそれぞれの型に変換します。
ListCompare 二つのリストの異同を評価します。
ListItemDelete リストからひとつの要素を削除します。
ListKeyCase 入力に基づいてリスト全体の値を大文字または小文字に変更します。
ListToWholeNumbers float 型データのリストを、integer 型データのリストへ変換します。
ListXorY 二つのリストを連結します。ただし、要素の重複は排除します。
ListXandY 二つのリストを評価し、双方で共通する要素だけ返します。
ListXnotY リスト X に有り、リスト Y に無い要素だけ返します。
ListXxorY リストの片方にだけある要素を返します。重複した要素は、一方だけ除かれるのでなく、両方とも除かれます。
ListXequY 二つのリストが同一かどうか評価します。
ListXneqY 二つのリストが不一致かどうか評価します。
Replace リストの中の指定した要素を、一つだけ別の指定要素に置き換えます。
Replace All 'src list' において、'from list' に該当する要素を全て 'to list' に変換します。上の変換関数ほど単純ではありませんが、複数の要素を一度に処理できます。
ListUnique リストの中で一度だけ出現する要素を返します。
ccFixListDatatypes リストの各要素を適切に型キャストして返します。文字列から切り分けたリストを llSetPrimitiveParams 関数へ渡す際などに役立ちます。
2D Pseudo-Array 2次元配列の動作を模倣する方法