Arrays (Papyrus)/ja

From the CreationKit Wiki
Jump to navigation Jump to search

説明[edit | edit source]

Array(配列)は変数の特別な種類であり、同じタイプの1つ以上の変数を保持できます。あなたは数値インデックス(「0」~「その配列の長さ」-1の幅を通じて欲しい値を選択します(0から始まっているので、配列の長さに-1が必要)。


配列の更なる理解の獲得をしたいビギナーの為に[edit | edit source]

概要の簡易な説明は配列の限定された定義付け(理解)として利用できますが、現実の配列のコンセプトはもう少し深いものです。ですが、逆にシンプルな方がその概念を掴みやすいともいえます。例えとしてあなたは10個の武器が欲しいとしましょう。あなたは以下のようにするかもしれません : Weapon weapon1, Weapon weapon2 ...Weapon weapon10.これは退屈な作業だと思いませんか?そしてそれら1つ1つの変数が、あなたの求める条件を満たしているか、毎回チェックしなければならないのを想像してみてください。これらの苦痛を解決する方法を模索してみましょう。それらは全て「Weapon」タイプの変数であり、それらは内容(ある種の値)と該当武器の個数指定が異なるだけです。これらを結合し、使用するのに便利なものにしましょう。このような場合に役立つのが配列であり、特定のデータタイプの変数のセットであり、それらにはインデックス(索引)がついています。

☆ 簡単な式の説明: <identifier><index_or_count>

配列は同じタイプのデータ構造の集合体です。C言語からの発展と絡めてもう少し詳しく説明していきましょう : C言語だとこの武器については「weapon 10」としますが、配列を用いる場合、weapon[10] とします。この場合、「Index(インデックス)」又は「Count(カウント)」は「Identifier(ID)」と切り離されているので、あなたはインデックスでその配列の全てのweponにアクセスできます。weapon[i] - これは前のものとあまり変わってないように見えるかもしれません。しかしこれにより比較や変更等のため、言語(for, whileなどのループ構文)の中で繰り返す様々なフロー制御構造の中で、このインデックス[i](全てのインデックス)を使用することができます。 インデックス(全てのインデックス)できるを使用できます。これは変化するインデックスを持つ記述名の連結によって、ランタイム時にIDを構成する必要があるので、最初からアプローチすることはできません。

全ての変数、関数、データ構造などのほとんどは、何らかの方法により、プログラミング言語で記述をしなくては命令を出すことはできません。様々なデータタイプの変数は、人間のアイディアと、あなたのマシンの血管を走る1と0の解釈です。それらの1と0(bits -> binary digits)はバイトと呼ばれる8ビット単位にされました。1バイトはコンピューターのメモリの中にある、最も小さいアドレス指定可能な構造体であり、それは値(変数の最初のバイトのメモリアドレス)を読み書きする全ての変数を決定します。次にコンピューターの命令により、あなたのデータタイプがどのくらいの大きさか調べられます。配列というものは、メモリ的にいうと、ちょうど一緒にスタックされているバイトの束であり、メモリ(これは仮想メモリのことで、物理メモリのことではありません)の連続です。これから先この仮想メモリーのことをただのメモリと呼びます。ただ、物理メモリのレイアウトが必ずいつも連続しているわけではないのは、少し覚えて置いてください。システムがその変数のデータータイプ(変数の名前、ID、インデックスなど)を知っている場合、それは望んだ要素の最初のバイトのメモリアドレスを正確に計算できます。先ほど説明したバイトが最も小さいアドレス指定可能単位といったとおり、それはアドレス指定がなされています。あなたは「44B, New York」に住んでいる等の住所指定の場合のように、メモリの1バイトは、コンピューターが追跡に使用する、その追加された数値を得ることができます。メモリは連続しており、そこにはバイトと呼ばれる家々の長いストリートがあります(これは日本的に言えば、バイトという数値を基準とした「土地の区画」の話です。ニューヨークのストリートは日本と異なり、家と道が明確に対応しており正確な碁盤の目状なので、配列の説明に使われたのだと思われます)。

1つのデータタイプは1バイト(range (2^8)-1 -> 256 - 1 又は range [0,255] (unsigned))と同じくらいシンプルになれます。注意するのは、私達のシステムにおいて、バイトはよくChar(Characterの略称)と呼ばれ、256項目の区域内にフィットしています(これは最も小さいもの(Char型は最も小さいサイズの型)なので、スクリーンにテキストを表示するには別の多くの方法が存在します)。そして私達は古典的な32ビット(4バイト)を使用しています。ここでは配列名を「anArray」として、10の要素からなる配列の6番目の要素を取り出そうとしていると仮定し、以下配列の説明をしていきます。(いきなり「要素」という言葉が出てきましたが、例えば「anArray("日", "月", "火", "水")」という配列があるとしたら、要素はここでいう「日、月、火、水、」のことです。今回は例えばその要素の「水」だけを取り出そうとしています)

覚えておくべきことは、配列名「anArray」は全体の配列(これらは連続して、隣接されているものです)の最初のメモリアドレスを指しているということです。そして全体の配列の最初の1バイトは最初の「要素」でもあります(例えば上の例だと「anArray(日)」)。各32ビット整数は4バイトのスペースを取るので、10要素がある配列は4×10で40バイトの容量があります。単純に言えば連続した40メモリーアドレスがあるということです。各要素が4バイトのスペースを取るならば、最初から6番目の要素の最初のバイトはいくらでしょうか?6×4で24バイトでしょか?答えをいうと、実際には24バイトは7番目の要素の最初のバイトです。この理由は要素は「0」からそのインデックスがはじまっているからです(これがページの一番最初で、マイナス1をしろと言った理由です)。私達は配列から21番目、22番目、23番目のバイトをオフセット(4バイトのデータ構造における特定の距離)、基本メモリアドレス(保持する変数から分析される)、必要な要素インデックスに基づいて取得することができます。

これは「weapon1, weapon2, weapon3 etc」のような細切れになっている変数の「セット」を追跡するよりはるかに簡単です。そして次のアドレスを検索するには、その値の変数のメモリアドレスを分析することや他の要素周りを手動で捜索するよりも、(配列の最初のバイトである)基本メモリアドレスにオフセット(データ構造における最初のバイトからの特定の距離)を加える方が簡単です。例えば4バイト(アクセスを望むweapon2のメモリアドレス(つまり最初のバイト)によって、weapon1のメモリアドレスをオフセットしてみる場合、あなたがそのメモリアドレスを「ロックオン」するというのは、実際にはChar型変数の最初のバイトを意味し、システムは整数としてそれをキャストするでしょう。これに対して3バイトは、それは完全に異なる変数に属するメモリレーンよりもさらに遠いものとなります。この変数を読もうとするならばわけの分からないものを与え、それを変えると、不可逆的に定義されていないあなたのアプリケーションのメモリ・スペースは崩壊するでしょう。あなたはちっぽけな人間のデータタイプ要素についてコンピューターが理解できないとなぜ指摘したか理解すべきです。コンピューターは理解できるかどうかに関わらず、あなたの命令を実行してしまうでしょうから。

ここまでが基本であり、配列のコンセプトの背景である概略と論理とメモリ序論についてです。これは非常に広範囲な話題であり、より知識を広げたあと、ここでの簡素化された説明をもう一度見返すことを推奨します。


配列の宣言[edit | edit source]

 float[] myFloatArray
 ObjectReference[] myObjectArray = new ObjectReference[10]

myFloatArrayはフロート型の空の配列を宣言しています。それは「0」の長さがあり、「None」とイコールです。myObjectArrayもそれと同じ方法で宣言されています(ただし、それはObjectReferenceオブジェクトの配列である点で異なります)。そしてObjectReferenceオブジェクトの新しい配列についての割当ては、10要素あります(全てそれらはデフォルト値である「None」で始まります)。注意するのは関数の中でのみ新しい配列が呼べるということです。もしあなたが空の配列を持って始まる変数を欲しいならば、OnInitイベントの中に配列を作ることができます。

新しい配列を作るには、全てのイベントと関数がそれにアクセスできるよう、空のスクリプトにそれをおく必要があります(Thanks to DreamKing):

Form[] MyArray
 
Event OnInit()
 MyArray = New Form[20]
EndEvent

配列の配列や多次元配列はできないので注意してください。ただ複数のWhileループを使用してその「フェイク」を作ることはできます(これについては下の方でまた説明します)。


Property配列を作ることは可能であり、エディターはそのための特別なUIを持っており、その中で追加・削除・再配列が可能です。

サンプル :

 Weapon[] Property MyWeapons Auto

注意 : 配列の長さを指定するのに変数や式は使用できません。それは整数で宣言する必要があります。

エラーサンプル:

  int NoOfItems = 10
  ObjectReference[] MyItems = new ObjectReference[NoOfItems] ; この行は動きません

関数パラメーター[edit | edit source]

関数でパラメータとして配列にアクセスするには、配列を宣言したのと同じ構文を使用してください。

サンプル :

 Function MyArrayFunction(int[] myArray, bool someOtherParameter)
 EndFunction


関数の戻り値[edit | edit source]

関数から配列に戻すには同じ構文を使用してください。

サンプル :

 int[] Function MyReturnArrayFunction()
   int[] someArray = new int[10]
   return someArray
 endFunction

配列の作成[edit | edit source]

配列を作成するにおいて、以下の配列の要素タイプによる「新しい」キーワードを使用し、括弧で配列のサイズが決定します。配列のサイズは「1と128の間の整数」でなければなりません(変数では駄目です)。言い換えると配列のサイズは、コンパイル時にセットされていなければなりません。配列のあらゆる要素は、その要素のデフォルト値(0、False、Noneのどれか)にセットされます。

サンプル :

 new bool[5]
 new Weapon[25]

通常、あなたは直接新しい配列変数にそれらを割当てるでしょうが、関数における配列を望んでいる場合、関数呼び出しで新しい配列を作ってください。

サンプル :

 MyArrayFunction(new int[20])

要素の取得/設定[edit | edit source]

配列から1つの値を得る/設定するには要素のインデックスにおける括弧を使用し、その後に、イコールと変数の名前を書いて、代入してください。インデックスは整数の変数や生の整数、式の結果かもしれません。有効な値の範囲は「0」からその配列の長さ(-1)までです。

サンプル :

 myArray[20] = newValue
 someRandomValue = myArray[currentIndex]
 myArray[i * 2] = newValue

配列要素が他のスクリプトである場合、同じようにPropertyと関数にアクセス可能です。

サンプル :

 DoorArray[currentDoor].Lock()
 objectXPos = ObjectArray[currentObject].X

注意するのは、配列はリファレンスによってパスと割当がなされるので、配列要素の変更は、配列を見るほかのどんな変数にも反映されます。


Getting Length(長さの取得)[edit | edit source]

Length Propertyを呼ぶことで配列の長さを簡単に得ることができます。配列に「None」が割当てられている場合、その長さは「0」になります。

サンプル :

 int ArrayLength = myArray.Length

配列の割当て/パス[edit | edit source]

変数にするように、別の配列に配列を割当てるか、又は関数に配列をパスすることはできますが、オブジェクトのように、リファレンスによって配列はパス/割当がなされているのに注意してください。言い換えれば、配列を別の配列に割当てる場合、両者は同じ配列を見ていることになり、1つを変えると、もう片方もその変更が適用されることに注意してください。

サンプル :

 int[] Array1 = new int[5]
 int[] Array2 = Array1     ; Array1とArray2は同じ配列を見ています!
 Array1[0] = 10
 Debug.Trace(Array2[0])    ; あなたはArray1上の値を変えましたが、Tracesも"10"を出力します

参照されなくなった配列は破壊されます。 サンプル :

 int[] Array1 = new int[5]
 int[] Array2 = new int[10]
 Array2 = Array1     ; 今Array1とArray2は同じ「5」要素を見ます。そしてArray2の「10」要素は破壊されます

配列のキャスティング[edit | edit source]

配列は文字列又はブールにキャストできます。文字列に配列をキャストする場合、コンマで区切られた括弧の中の配列に各要素を置く事になります。配列が特に長い場合、文字列が整えられ、後ろの文字が省略された形で置かれるかもしれません。ブールに配列をキャストする場合、それは「True」となり、長さが「0」ではない場合も「True」となり、長さが「0」の場合「False」になります。違うタイプの配列を配列にキャストすることはできません(例えその要素のキャストに成功するとしてもです)

サンプル :

 Debug.Trace(MyArray)    
; Tracesは"[element1, element2, element3]"又は配列がとても大きい場合"[element1, element2, ...]"となります
 if (MyArray)
   Debug.Trace("Array has at least one element!")
 else
   Debug.Trace("Array has no elements!")
 endIf

配列のサーチ[edit | edit source]

異なる2つの方法(「Find」と「RFind」を呼ぶ)を使用して配列を探します。「Find」はそれを与えた要素からその配列を見つけるため、要素の一番最初からサーチを始め(デフォルトはelement 0です)、その要素が見つかるまで(それが目的の配列にマッチするとわかるまで)その配列の最後の要素までサーチは続けられます。「RFind」は同じ方法でサーチしますが、要素の一番最後からサーチを始め(デフォルトは -1です。これはその配列の長さの-1という意味であり、それはつまりその配列の一番最後の要素を表します)、その配列の一番先頭に向かってサーチをしていきます。該当項目がないとそれはインデックスのためにマイナス値を返します。

「Find」構文 :

int Function Find(;/element type/; akElement, int aiStartIndex = 0) native

「RFind」構文 :

int Function RFind(;/element type/; akElement, int aiStartIndex = -1) native

サンプル :

; サンプルのために配列をセットします
string[] myArray = new string[5]
myArray[0] = "Hello"
myArray[1] = "World"
myArray[2] = "Hello"
myArray[3] = "World"
myArray[4] = "Again"

; 出力 "配列に存在しません!"
if myArray.Find("Whee!") < 0
  Debug.Trace("Whee! does not exist in the array!")
else
  Debug.Trace("Whee! exists in the array!")
endIf
 
; 出力 "The first Helloはposition 0にあります"
Debug.Trace("The first Hello is at position " + myArray.Find("hello"))
 
; 出力 "The last Helloはposition 2にあります"
Debug.Trace("The last Hello is at position " + myArray.RFind("hello"))
 
; 出力 "The first Hello in or after position 2はposition 2にあります"
Debug.Trace("The first Hello in or after position 2 is at position " + myArray.Find("hello", 2))

; CKか他の方法で設定します
MagicEffect[] Property EffectList Auto
 
Event OnMagicEffectApply(ObjectReference akCaster, MagicEffect akEffect)
  if SpellList.Find(akEffect) < 0
    Debug.Trace("Hit by a spell we care about")
  else
    Debug.Trace("Ignoring spell hit")
  endIf
EndEvent
 
Event OnHit(ObjectReference akAggressor, Form akSource, Projectile akProjectile, bool abPowerAttack, bool abSneakAttack, \
  bool abBashAttack, bool abHitBlocked)
  ; コンパイルしません。両立しないタイプの値で配列を探す事はできません
  if SpellList.Find(akProjectile) < 0
    Debug.Trace("Hit by a projectile we care about")
  endIf
EndEvent

共通のタスク[edit | edit source]

全ての要素に何かをする[edit | edit source]

配列の全ての要素に何かしたい場合がよくあるかもしれません。これはカウンターでWhileループをセットし、そのあと各要素に何かをします。

サンプル:

Function DisableAll(ObjectReference[] akObjectArray) Global
	Int iElement = akObjectArray.Length
	While iElement
		iElement -= 1
		akObjectArray[iElement].Disable()
	EndWhile
EndFunction


Whileループは、その要素が「0より大きい」場合のみ動くのに注意してください。これは有効な要素は「長さ-1」なので0より小さいことはありえないからです。もしあなたが、Whileループに「>=」を使用する場合、最後の要素でエラーが起きるでしょう。なぜなら「要素 -1」は存在しないからです。


特定の要素をカウントします[edit | edit source]

配列にあるXを数えたい場合があるかもしれません。その場合「Find」とWhileループでそれをすることができます。各「Find」がその位置(以前見つけた要素の位置)の後からスタートするのを確実にしてください。以前の位置から「1」過ぎるのを確実にしないと、同じ要素をずっと発見し続け、立ち往生することになります。

配列にあるhelloの数を数えます
int currentPosition = myArray.Find("hello")
int count = 0
while currentPosition >= 0 ; それ以上見つからなくなるまでループ(positionが0より少なくなるまでループ)
  count += 1
  currentPosition = myArray.Find("hello", currentPosition + 1) ; +1 により同じものを再び見つけません
endWhile
 
Debug.Trace("There are " + count + " hellos in the array")


多次元配列のフェイク[edit | edit source]

4つのInt Property配列(要素は0~9)があると仮定してみましょう。以下のサンプルでは「0000」から「9999」に増加させ、それを出力しています:

Int Array1Element = 0
While (Array1Element < Array1.Length)
  Int Array2Element = 0
  While (Array2Element < Array2.Length)
    Int Array3Element = 0
    While (Array3Element < Array3.Length)
      Int Array4Element = 0
      While (Array4Element < Array4.Length)
        debug.notification(Array1[Array1Element] + Array2[Array2Element] + Array3[Array3Element] + Array4[Array4Element])
        Array4Element += 1
      endWhile
      Array3Element += 1
     endWhile
     Array2Element += 1
  endWhile
  Array1Element += 1
endWhile

少ない多次元配列の場合、上記のように複数の「1D(1次元)」配列は必要ありません。例えばパピルスでの配列サイズは128に制限されるので、3次元配列の場合、サイズは「5×5×5」以下の配列を1つしか使用することはできません。二次元の場合「11×11」が限界となります。1次元でもN次元でもメモリは変わらず、これらはアドレス指定の方法だけが唯一の違いです。アドレス指定は手動で実行できます。

以下のコードは1次元配列を使用した、3次元配列「5×5×5」のシミュレートです:

これは配列割当には使用せず、ローカルで参照に使用されるだけです
int arraySizeX = 5		; この要素は「X」軸です
int arraySizeY = 5		; この要素は「Y」軸です
int arraySizeZ = 5		; この要素は「Z」軸です
 
int[] arrayData
 
EVENT OnInit()
 
	arrayData = new int[125]	; 整数が要求されるので、複数の配列サイズパラメーターを手動
 
EndEVENT
 
; 3次元座標のXYZ要素から1次元配列要素を得る
int Function GetArrayIndexForCoordinates(int x, int y, int z)
 
	return (z * arraySizeX * arraySizeY + y * arraySizeX + x)
 
EndFunction
 
; 3次元座標を使用して整数配列に値をセットする
Function SetArrayElement(int x, int y, int z, int value)
 
	int index = GetArrayIndexForCoordinates(x, y, z)
	if(index < arrayData.Length)
		arrayData[index] = value
	Endif
 
EndFunction
 
; XYZのソートオーダーを配列を通じ最速で反復します
Function IterativeFunctionLinear()
 
	int i = 0
	while(i < arrayData.Length)
		SomeOtherFunction(arrayData[i])	; パスされた要素を使って関数で何かします
		i += 1
	endWhile
 
EndFunction
 
; ZYXソートオーダーをゆっくり反復します
Function IterativeFunctionLooped()
 
	int iX = 0
	while(iX < arraySizeX)
		int iY = 0
		while(iY < arraySizeY)
			int iZ = 0 
			while(iZ < arraySizeZ)
				int index = GetArrayIndexForCoordinates(iX, iY, iZ)
				if(index < arrayData.Length)
					SomeOtherFunction(arrayData[index])	; 関数で何かする
				Endif
				iZ += 1
			EndWhile
			iY += 1
		EndWhile
		iX += 1
	EndWhile
 
EndFunction
 
Function SomeOtherFunction(int value)
 
	; 値を使用した関数を実行します。例えば、整数の変わりに配列のフォームを格納し、何かします

Creating a FormID Array(FormID 配列の作成)[edit | edit source]

Intの変数とPropertyは16進数FormIDにセットできます。これは「GetFormID」, 「GetForm」, 「GetFormFromFile」を使用する時、特に効果的です。以下のものは動きますが、CKでFormIDとしてInt[]要素をセットすることはできません。

Int iFormID = 0x00000BEE
Game.GetPlayer().PlaceActorAtMe(Game.GetFormFromFile(iFormID, "Killer Bees.ESM") As ActorBase)

「0x」表記はパピルス・コンパイラー特有であり、あなたのために、この16進数は10進数に変換されるので、上記は意図されたものとしてちゃんと動きます。ただ、CKで事前定義されたエレメントを持ったFormIDの配列を格納することをあなたが望んでいる場合、あなたは先に16進数から10進数にそれを変換する必要があるでしょう。「プログラマー」モードのウィンドウズの計算機は簡単にこれを変換してくれるでしょう。例えば16進数の「BEE」は10進数の「3054」です。

Int[] Property iFormIDArray Auto ; 16進数から10進数に変換されたアクターFormIDで満たされます
Game.GetPlayer().PlaceActorAtMe(Game.GetFormFromFile(iFormIDArray[0], "Killer Bees.ESM") As ActorBase)

FormLists[edit | edit source]

FormLists can be used to create an array of properties/objects for a script:

FormList Script Example: Disabling/enabling a large quantity of objects easily

See Also[edit | edit source]



Language: [[::Arrays (Papyrus)/ja|English]]