[ 初めての方へ | 一覧(最新更新順) | 全文検索 | 過去ログ ]
『再帰処理について』(とと)
functionの再帰処理で困っています。
マクロのコードでどこを修正したらいいかアドバイスいただけたらと思います。
まずどのような処理をするか説明しますと、2桁の整数(10〜99)をランダムに二つ作って、大きい数から小さい数を引き算するひっ算をエクセルシート上にいくつも作り出すことです。これだけのことなら実現できたのですが、例えば53−23とか、49−43のように二つの整数の同じ桁の数値が同じだと引き算として簡単になってしまうので、これを回避するためにランダムに作り出した二つの整数の各桁の数値が同じ場合は、再帰処理で整数を作り直すことを考えました。
以下はそのコードです。
'=================================================================
' 10〜99の整数を二つ生成する(引き算用)
'=================================================================
Public Function 整数2桁を生成_引き算() As Long()
Dim tmp(1 To 2) As Long, tmp0 As Long
Dim cnt As Long
For cnt = 1 To 2 Randomize tmp(cnt) = Int(90 * Rnd + 10) Next cnt
'大きい数値をtmp(1)のほうにする If tmp(1) < tmp(2) Then tmp0 = tmp(1) tmp(1) = tmp(2) tmp(2) = tmp0 End If
'二つの整数の同じ桁の値が同じ場合は再生成する If tmp(1) Mod 10 = tmp(2) Mod 10 _ Or tmp(1) \ 10 = tmp(2) \ 10 Then ⇒ Call 整数2桁を生成_引き算 Exit Function End If
整数2桁を生成_引き算 = tmp()
End Function
'=================================================================
コード中の⇒の部分で再帰処理に入り、tmp(1),tmp(2)の整数が改めて作られるのに、その値は戻り値として採用されず、呼び出し元のプロシージャで「インデックスが有効範囲にありません」とエラーになってしまいます。
どうしたら再帰処理がうまくいくのでしょうか?
ほかの方法でもいいので解決のアドバイスいただけたら幸いです。
< 使用 Excel:Excel2021、使用 OS:Windows11 >
もう1つ、戻り値としてメモリ上に残る変数を用意してみては、どうでしょう?
Public Function 整数2桁を生成_引き算() As Long() Static ret(1 To 2) As Long '戻り値用の変数 Dim tmp(1 To 2) As Long, tmp0 As Long Dim cnt As Long For cnt = 1 To 2 Randomize tmp(cnt) = Int(90 * Rnd + 10) Next cnt '大きい数値をtmp(1)のほうにする If tmp(1) < tmp(2) Then tmp0 = tmp(1) tmp(1) = tmp(2) tmp(2) = tmp0 End If '二つの整数の同じ桁の値が同じ場合は再生成する If tmp(1) Mod 10 = tmp(2) Mod 10 _ Or tmp(1) \ 10 = tmp(2) \ 10 Then Call 整数2桁を生成_引き算 Else ret(1) = tmp(1): ret(2) = tmp(2) End If 整数2桁を生成_引き算 = ret End Function (まる2021) 2023/09/12(火) 07:25:41
再帰を使わず、希望結果が出るまで無限ループするのでも
Public Function 整数2桁を生成_引き算() As Long() Dim tmp(1 To 2) As Long, tmp0 As Long Dim cnt As Long Do For cnt = 1 To 2 Randomize tmp(cnt) = Int(90 * Rnd + 10) Next cnt '大きい数値をtmp(1)のほうにする If tmp(1) < tmp(2) Then tmp0 = tmp(1) tmp(1) = tmp(2) tmp(2) = tmp0 End If '二つの整数の同じ桁の値が同じ場合以外はループを抜ける If Not (tmp(1) Mod 10 = tmp(2) Mod 10 _ Or tmp(1) \ 10 = tmp(2) \ 10) Then Exit Do End If Loop 整数2桁を生成_引き算 = tmp End Function (まる2021) 2023/09/12(火) 07:36:34
関数の結果をどうやって受けるかっていうのはいくらかやり方がありますが、
Function sampleFunc() As String sampleFunc = "文字列を返します" End Function
変数で受け取るのがよくやるやり方です
Sub test1() Dim ret ret = sampleFunc '← 関数の返値を変数に代入 Debug.Print ret End Sub
Callしただけでは、結果を受け取れません
Sub test2() Dim ret Call sampleFunc '← 何も起こらない Debug.Print ret End Sub モジュールレベルの変数に代入するという方法もありますが。 (´・ω・`) 2023/09/12(火) 08:59:11
上記を踏まえて、こうなります。 Sub test() Dim ret() As Long ret = 整数2桁を生成_引き算 Debug.Print ret(1); ret(2) End Sub
Public Function 整数2桁を生成_引き算() As Long() ReDim tmp(1 To 2) As Long Dim tmp0 As Long Dim cnt As Long Randomize 'Randomize は1回でいいです For cnt = 1 To 2 tmp(cnt) = Int(90 * Rnd + 10) Next cnt '大きい数値をtmp(1)のほうにする If tmp(1) < tmp(2) Then tmp0 = tmp(1) tmp(1) = tmp(2) tmp(2) = tmp0 End If '二つの整数の同じ桁の値が同じ場合は再生成する If tmp(1) Mod 10 = tmp(2) Mod 10 _ Or tmp(1) \ 10 = tmp(2) \ 10 Then tmp() = 整数2桁を生成_引き算() End If 整数2桁を生成_引き算 = tmp() End Function
まる2021さんの提案のとおり、再帰はループで書き換え可能な場合が多いです 再帰を使って何がうれしいかというと... まぁうれしいこともあるのですが、 今回の場合は、再帰を使わない方が簡単な気がします (´・ω・`) 2023/09/12(火) 09:10:59
書き忘れました >呼び出し元のプロシージャで「インデックスが有効範囲にありません」とエラー この理由は、
最後に 整数2桁を生成_引き算 = tmp() とする前に Exit Funtion しているからです。返値が空になります。 (´・ω・`) 2023/09/12(火) 09:36:42
こんな風でした。重なっているので、無駄かもしれませんん。折角書いてしまったので。 Sub test() '二つの整数の同じ桁の値が同じ場合は再生成する Dim tmp Dim check As Boolean Randomize Do tmp = 整数2桁を生成_引き算 check = tmp(1) Mod 10 = tmp(2) Mod 10 _ Or tmp(1) \ 10 = tmp(2) \ 10 Loop While check Debug.Print tmp(1); tmp(2) End Sub
Public Function 整数2桁を生成_引き算() As Long() Dim tmp(1 To 2) As Long, tmp0 As Long Dim cnt As Long For cnt = 1 To 2 tmp(cnt) = Int(90 * Rnd + 10) Next cnt '大きい数値をtmp(1)のほうにする If tmp(1) < tmp(2) Then tmp0 = tmp(1) tmp(1) = tmp(2) tmp(2) = tmp0 End If 整数2桁を生成_引き算 = tmp End Function
(xyz) 2023/09/12(火) 10:01:15
Sub test() Dim tmp(1 To 2) As Long, i As Long
For i = 1 To 100 Randomize tmp(1) = Int(90 * Rnd + 10) Do Randomize tmp(2) = Int(90 * Rnd + 10) If tmp(1) Mod 10 <> tmp(2) Mod 10 And tmp(1) \ 10 <> tmp(2) \ 10 Then Exit Do Loop
ActiveSheet.Cells(i, "A").Value = WorksheetFunction.Max(tmp) & " − " & WorksheetFunction.Min(tmp) & "= ?" Next i End Sub
(もこな2 ) 2023/09/12(火) 15:00:20
蛇足ですが一応書いておきます。
VBAの乱数は疑似乱数です。乱数を漸化式で求めています。乱数系列と呼びます。 乱数系列はシード値と呼ばれる値を元に計算しています。同じシード値であれば同じ順序で値が出てきます。
Rnd関数は、1回呼び出される毎に、この乱数系列の次の値を返します 厳密な乱数が必要な計算ではよい乱数ではないとの指摘もありますが、 実用上十分にランダムな数値の並びになっています。
Randmoize関数は、シード値を変更=関数系列 を変更するものです。
1つの乱数系列を順に見ていけば、乱数として成立しています。 乱数系列を都度変えて、系列の最初の値を次々見ていくという方法は、乱数と成立しているか不明です。
というわけで、Randomize はRndを呼ぶループの前に一回だけ実行するのがよいです。 (´・ω・`) 2023/09/12(火) 17:15:47
[ 一覧(最新更新順) ]
YukiWiki 1.6.7 Copyright (C) 2000,2001 by Hiroshi Yuki.
Modified by kazu.