UWSC|配列要素の削除と挿入・追加【参照渡し使用】

ポク太郎です。

プログラムはサラッと短いコードで見やすくするのが基本。理由は、全ステップに意味があるので、これは何だっけ?と常に再考する必要があるから。

そのためによく使うのはループ→それにはやはり要素番号指定だけで使える配列変数が便利。

というわけで、配列変数を使いこなすため、サッと配列の指定要素を削除したり、挿入・追加して配列を再構築してしまう方法を考えています。


関数への値渡しと参照渡し

図を二つ。処理の順番と変数更新のタイミングを書いた図です。

左側のメインから右側の関数へ引数を渡しますが、渡し方に“値渡し”と“参照渡し”(C++と同じものと思ってもいいのかな?)があります。

値渡し

関数側定義:FUNCTION 関数名(引数名)
[UWSC]配列の要素の削除と挿入・追加[参照渡し使用]【自作関数】

関数へはメインのローカル変数A値を教えて、関数内RESULTの値をメインのローカル変数Bに代入します。

このとき、関数は変数Aの値を教えてもらってるだけなので、関数内からはメインの変数Aを一切更新することができません。

これは、変数名が被っても構わない→複数人開発でもバグが出にくい方法としてC言語辺りから主流になった方法。

参照渡し

関数側定義:FUNCTION 関数名(var 引数名)
[UWSC]配列の要素の削除と挿入・追加[参照渡し使用]【自作関数】

関数へはメインのローカル変数Aそのものを渡して(在り処を教えてる?)、関数内RESULTの値をメインのローカル変数Bに代入します。

このとき、関数はメインのローカル変数Aそのものを預かっている(に等しい)ので、関数内で引数Cを更新すると、メインのローカル変数である変数Aも更新されてしまいます。

C言語で“ポインタ渡し”が実装され(記憶位置であるアドレスを教えるので関数内から外の変数を触れる)、更に使いやすいものとして新環境(C++等)で採用された方法。詳しくは理解できてないのであしからず。

この参照渡しを実現するには、関数の宣言文FUNCTIONでの引数の定義を“var 引数”とします。

通常は値渡しを使用。でも都合の悪い場合=「戻り値が配列、かつ、要素数が関数内で変化してしまった場合」が存在。

UWSCでは下の一文書くだけで関数の戻り配列が全要素コピーされます。

hairetu =関数名(hairetu)

今回の目的は配列の再構築=要素の削除、挿入・追加→関数を通る前後で要素数が変化。その場合、「要素数が違うよ」とエラーが出て止まってしまいます。

そこで、この“参照渡し”を使用し、呼び出し側が一行で済むように作ります。

以下が注意ポイント。爺の古臭い感覚なので、近くにプロがいる場合は確認してみて下さい。
C言語が賞賛された理由はすべて独立な関数で構成し、他人数開発に移行できるから→関数内からメインの変数を書き換えできちゃう方法はできる限り排除すべき⇒「“参照渡し”使用したのはこの部分だけ」と“特殊扱い”した部分と常に思い出せる重要部にしか使わないよう努めます。

配列の要素の削除remove()関数

筆者はFUNCTIONしか使わない人なので、関数の戻り値にダミー値(エラー:0、正常終了:1)を返しています。概念的には戻り値の無いPROCEDUREで構成する方が分かりやすいと思います。
FUNCTION remove(var a[],rn)
   IF rn<0 or rn>LENGTH(a)-1
            PRINT "ポクエラー(remove関数):無効な要素番号が削除指定されました。"
            RESULT=0
   ELSE
      FOR i=rn TO LENGTH(a)-1-1
            a[i]=a[i+1]
      NEXT
            RESIZE(a,LENGTH(a)-1-1)
            RESULT=1
   ENDIF
FEND
関数プログラム説明
1,12行目1
12
FUNCTION remove(var a[],rn)
FEND
remove関数宣言:引数rnの要素番号を渡された配列から削除する関数
引数a[]: 再構築する配列
引数rn: 削除する要素番号
2,5,11行目2
5
11
   IF rn<0 or rn>LENGTH(a)1
   ELSE
   ENDIF
引数rnが配列の有効な要素数を指定しているかを判定。
無効な要素番号(-値や最大要素番号より大)⇒3、4行目へ
有効な要素番号⇒6~10行目へ
3,4行目3
4
            PRINT ポクエラー(remove関数):無効な要素番号が削除指定されました。
            RESULT=0
エラーを表示し、関数の戻り値は0を返す。
6~10行目6
7
8
9
10
      FOR i=rn TO LENGTH(a)11
            a[i]=a[i+1]
      NEXT
            RESIZE(a,LENGTH(a)11)
            RESULT=1
引数rnから最大要素番号の一つ手前までをループし、一つ上の要素番号の内容を配列a[i]に代入していきます。

済んだら配列を最大要素番号より一つ小さくリサイズします。
RESIZE(a,現在の最大要素番号-1)

関数の戻り値として1を返します。


配列の要素の挿入・追加append()関数

筆者はFUNCTIONしか使わない人なので、関数の戻り値にダミー値(エラー:0、正常終了:1)を返しています。概念的には戻り値の無いPROCEDUREで構成する方が分かりやすいと思います。
FUNCTION append(var a[],an,dt)
            RESIZE(a,LENGTH(a)-1+1)
   IF an<0 or an>LENGTH(a)-1
            a[LENGTH(a)-1]=dt
   ELSE
      FOR i=LENGTH(a)-1 TO an step -1
         IF i=an
            a[i]=dt
         ELSE
            a[i]=a[i-1]
         ENDIF
      NEXT
   ENDIF
            RESULT=1
FEND
関数プログラム説明
1,15行目1
15
FUNCTION append(var a[],an,dt)
FEND
append関数宣言:引数anの要素番号位置に配列要素を挿入・追加する関数(引数anに-値や最大要素番号より大を指定された場合は最後に追加する仕様)
引数a[]: 再構築する配列
引数an: 挿入する位置の要素番号
引数dt: 要素番号anの位置に入れたい内容
2行目2            RESIZE(a,LENGTH(a)1+1)
まず、配列の最大要素番号を現在より一つ大きくリサイズします。
RESIZE(a,現在の最大要素番号+1)
3,5,13行目3
5
13
   IF an<0 or an>LENGTH(a)1
   ELSE
   ENDIF
引数anが配列の有効な要素数を指定しているかを判定。
無効な要素番号(-値や最大要素番号より大)⇒4行目へ
有効な要素番号⇒6~12行目へ
4行目4            a[LENGTH(a)1]=dt
現在の最大要素番号の位置(2行目で既に一つ大きくしているので)に引数dtを代入します。
6~12行目6
7
8
9
10
11
12
      FOR i=LENGTH(a)1 TO an step 1
         IF i=an
            a[i]=dt
         ELSE
            a[i]=a[i1]
         ENDIF
      NEXT
最大要素番号から引数anまでをループします。(step -1として大きい方から順番に)

引数anのときは引数dtを代入、違うときは一つ下の要素番号の内容を配列a[i]に代入していきます。

14行目14            RESULT=1
関数の戻り値として1を返します。


配列要素の削除と挿入・追加動作確認

動作確認用プログラムがこちら。

   DIM test[10]
FOR i=0 TO LENGTH(test)-1
   test[i]=i*2
   PRINT test[i]
NEXT
   PRINT "--最大要素番号は"+LENGTH(test)-1

   rn=8
   remove(test,rn)
   PRINT "--要素"+rn+"を削除"
FOR i=0 TO LENGTH(test)-1
   PRINT test[i]
NEXT
   PRINT "--最大要素番号は"+LENGTH(test)-1

   an=5
   dt=51
   append(test,an,dt)
   PRINT "--要素"+an+""+dt+"を追加"
FOR i=0 TO LENGTH(test)-1
   PRINT test[i]
NEXT
   PRINT "--最大要素番号は"+LENGTH(test)-1
動作確認プログラム説明
1~6行目1
2
3
4
5
6
   DIM test[10]
FOR i=0 TO LENGTH(test)1
   test[i]=i*2
   PRINT test[i]
NEXT
   PRINT 最大要素番号は+LENGTH(test)1
配列test[10]を準備し、要素番号x2の値を代入してテスト用配列を作成、表示します。最後に最大要素番号を表示しておきます。
8~14行目8
9
10
11
12
13
14
   rn=8
   remove(test,rn)
   PRINT 要素+rn+を削除
FOR i=0 TO LENGTH(test)1
   PRINT test[i]
NEXT
   PRINT 最大要素番号は+LENGTH(test)1
変数rnを設定して、作成したremove()関数を呼び出した後、完成したテスト用配列を表示します。最後に最大要素番号を表示。
16~23行目16
17
18
19
20
21
22
23
   an=5
   dt=51
   append(test,an,dt)
   PRINT 要素+an++dt+を追加
FOR i=0 TO LENGTH(test)1
   PRINT test[i]
NEXT
   PRINT 最大要素番号は+LENGTH(test)1
変数anと変数dtを設定して、作成したappend()関数を呼び出した後、完成したテスト用配列を表示します。最後に最大要素番号を表示。


動作確認結果がこちら。

0
2
4
6
8
10
12
14
16
18
20
--最大要素番号は10
--要素8を削除
0
2
4
6
8
10
12
14
18
20
--最大要素番号は9
--要素5に51を追加
0
2
4
6
8
51
10
12
14
18
20
--最大要素番号は10

要素8を削除すると、test[8]の16が無くなり最大要素番号が一つ小さく9となります。

要素5に51を追加すると、test[5]に51が代入、以降は後ろへ一つづつずれて、最大要素番号が一つ大きい10になってくれました。

8、16行目の値を変更して試してみてください。

配列変数の話題
配列の最大要素を返す関数関数の引数と戻値に配列変数【使い方まとめ】配列の要素の削除と挿入・追加【参照渡し】
本記事の内容は以下でした。
○配列の再構築のやり方

関数への値渡しと参照渡し
配列の要素の削除する関数
配列の要素の挿入・追加する関数

コメント

タイトルとURLをコピーしました