2017年10月15日日曜日

[UWSC]配列の要素の削除と挿入・追加[参照渡し使用]【自作関数】

UWSCという開発ソフトを使って作り上げる作業を実際にやってみるカテゴリ。準備した自作関数や使い方、トラブルを記事にしていきます。得るものがあれば幸いです。

記事の内容

○配列の再構築のやり方
関数への値渡しと参照渡し
配列の要素の削除する関数
配列の要素の挿入・追加する関数

スマホで閲覧されている方へ
このブログは画面横幅を広くしてあります。スマホ解像度での閲覧は厳しいと思いますので、ウェブバージョンへどうぞ。

ポク太郎です。

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

以前、下のような記事を書きましたが、すごく面倒な方法だったので作り直します。


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

図を二つ載せます。
プログラムの処理の順番と変数が更新されるタイミングを書いた図です。左側のメインから右側の関数へ引数を渡すのですが渡し方に“値渡し”と“参照渡し”(C++と同じものと思ってもいいのかな?)があります。

○値渡し
[UWSC]配列の要素の削除と挿入・追加[参照渡し使用]【自作関数】
関数へはメインのローカル変数A値を教えて、関数内のRESULTの値をメインのローカル変数Bに代入します。
このとき、関数は変数Aの値を教えてもらってるだけなので、関数内ではメインの変数Aを一切更新することができません。

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

○参照渡し
[UWSC]配列の要素の削除と挿入・追加[参照渡し使用]【自作関数】
関数へはメインのローカル変数Aそのものを渡して(在り処を教えてる?)、関数内のRESULTの値をメインのローカル変数Bに代入します。
このとき、関数はメインのローカル変数Aそのものを預かっている(に等しい)ので、関数内で引数Cを更新すると、メインのローカル変数である変数Aを更新することができます。

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

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


通常は値渡しを使用すればよいのですが、都合の悪い場合があります。戻り値が配列、かつ、要素数が関数内で変化してしまった場合です。

配列の要素数が関数を通る前後で変化しない場合は、UWSCでは下のように一文書いてやると、関数の戻り配列が全要素コピーされますが、
hairetu = 関数(hairetu)

関数内で配列の要素数が変わったときには、上のように書くと「要素数が違うよ」とエラーが出て止まってしまいます。

今回やろうとしているのは、配列の再構築。要素を削除、挿入・追加ですので、要素数が関数を通る前後で変わってしまいます。

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


配列の要素の削除

関数の戻り値として特に重要でない値(エラー:0、正常終了:1)を返すプログラムになっています。筆者はPROCEDUREを使わない人なのでFUNCTIONを使ってますが、概念的にはPROCEDUREで構成する方が分かりやすいと思います。
remove()関数
1
2
3


4
5
6
7
8
9
10
11
12
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)-1-1
            a[i]=a[i+1]
      NEXT
            RESIZE(a,LENGTH(a)-1-1)
            RESULT=1
引数rnから最大要素番号の一つ手前までをループし、一つ上の要素番号の内容を配列a[i]に代入していきます。

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

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



配列の要素の挿入・追加

関数の戻り値として特に重要でない値(ダミー:1)を返すプログラムになっています。筆者はPROCEDUREを使わない人なのでFUNCTIONを使ってますが、概念的にはPROCEDUREで構成する方が分かりやすいと思います。
append()関数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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[i-1]
         ENDIF
      NEXT
最大要素番号から引数anまでをループします。(step -1として大きい方から順番に)

引数anのときは引数dtを代入、違うときは一つ下の要素番号の内容を配列a[i]に代入していきます。
14行目14            RESULT=1
関数の戻り値として1を返します。



動作確認

動作確認
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
   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が無くなり最大要素番号が一つ小さくなります。要素5に51を追加すると、test[5]に51が代入、以降は後ろへ一つづつずれて、最大要素番号が一つ大きくなってくれました。

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


記事の内容

記事の内容は伝わりましたでしょうか。
○配列の再構築のやり方
関数への値渡しと参照渡し
配列の要素の削除する関数
配列の要素の挿入・追加する関数


【関連記事】
人気![UWSC]配列変数と関数[使い方まとめ]
[UWSC]配列の最大要素を返す関数









シェアされると小躍りして喜びます。

0 件のコメント:

コメントを投稿