C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[15]: C++からVBへの変換


(過去ログ 158 を表示中)

[トピック内 27 記事 (1 - 20 表示)]  << 0 | 1 >>

■91899 / inTopicNo.1)  C++からVBへの変換
  
□投稿者/ えんえん (1回)-(2019/08/08(Thu) 22:51:15)

分類:[.NET 全般] 

以下の再帰的なコードをVBに書き換えたいのですが
どのようにすれば良いでしょうか?


http://www.kurims.kyoto-u.ac.jp/~ooura/fftman/ftmn1_23.html#sec1_2_3


void fft(int n, double theta, double ar[], double ai[],
double tmpr[], double tmpi[])
{
int radix, n_radix, j, m, r;
double xr, xi, wr, wi;

for (r = 0; r < n; r += n_radix) {
fft(n_radix, theta * radix, &tmpr[r], &tmpi[r], ar, ai);
}
}


tmprとtmpiは一次元配列なのですが、
なぜか、要素数rを指定した上で、
サブルーチンに渡しています。
要素数を指定すると配列ではなく、doubleの値になってしまいますが、
なぜこのようなことができるのでしょうか?

変数の前の&で何か別のものに置き換えているのでしょうか?

https://ja.cppreference.com/w/cpp/language/operator_precedence

このページを見ると
アドレス取得、と書かれていますが、どういうことですか?


引用返信 編集キー/
■91900 / inTopicNo.2)  Re[1]: C++からVBへの変換
□投稿者/ 魔界の仮面弁士 (2290回)-(2019/08/08(Thu) 23:48:22)
2019/08/08(Thu) 23:52:48 編集(投稿者)

No91899 (えんえん さん) に返信
> int radix, n_radix, j, m, r;
> double xr, xi, wr, wi;

省略転記したために、使われていない変数や未初期化変数がチラホラ…。


> 以下の再帰的なコードをVBに書き換えたいのですが
> どのようにすれば良いでしょうか?

「ar As Double()」のかわりに、
「ar As ArraySegment(Of Double)」を使ってみては如何でしょう。

System.Span(Of T) 構造体を使える環境の場合は Span で。
引用返信 編集キー/
■91905 / inTopicNo.3)  Re[2]: C++からVBへの変換
□投稿者/ えんえん (3回)-(2019/08/09(Fri) 10:36:54)


ありがとうございます。



        For r As Integer = 0 To N - 1

            Dim ar2 As New ArraySegment(Of Double)(ar, r, r)
            Call FFT(・・・・,ar2,・・・・)

        Next

こうするという意味ですか?


        For i = 0 To 300

            Dim hhh = ar(i)

        Next

のようにして、配列と同じように値を取得できませんが
どのように使用すれば良いのでしょうか?


引用返信 編集キー/
■91906 / inTopicNo.4)  Re[3]: C++からVBへの変換
□投稿者/ 魔界の仮面弁士 (2291回)-(2019/08/09(Fri) 12:18:23)
No91905 (えんえん さん) に返信
> 配列と同じように値を取得できませんが
> どのように使用すれば良いのでしょうか?

ArraySegment は「大きな配列に対する一部の範囲のみ」を切り出した情報を管理します。
イメージとしてはこんな感じ。

Public Class ArraySegment
 Public Property Array As Double() '←管理対象となる配列そのものを表す

 Public Property Offset As Integer  'その配列のどの位置から
 Public Property Count As Integer   '幾つ分の要素を扱いたいのかを管理
End Class


使い方の例として:

Module Module1

  Sub Main()
    Dim dblArray As Double() = {11, 22, 33, 44, 55, 66, 77, 88}

    Dim ar1 As New ArraySegment(Of Double)(dblArray)

    Console.WriteLine("=== 中身を確認 ===")
    Test1_Dump(ar1)

    Console.WriteLine("=== 2〜5番目を切り出す ===")
    Dim ar2 = Test2_Slice(ar1)
    Test1_Dump(ar2)

    Console.WriteLine("=== 0 番目と 2 番目の値を入れ替える ===")
    Test3_Swap(ar2)
    Test1_Dump(ar2)

    Console.WriteLine("=== 1 番目の値を 10 倍にする ===")
    Test4_Modify(ar2)
    Test1_Dump(ar2)

    Console.WriteLine("=== 元の配列はどうなったか? ===")
    Array.ForEach(dblArray, AddressOf Console.WriteLine)

    Console.Write("*** 何かキーを押してください ***")
    Console.ReadKey()
  End Sub

  '中身を列挙してみる
  Sub Test1_Dump(a As ArraySegment(Of Double))
    Console.WriteLine("全要素数={0}", a.Array.Length)
    Console.WriteLine("開始位置={0}", a.Offset)
    Console.WriteLine("範囲の数={0}", a.Count)
    For Each dbl In a
      Console.WriteLine("... {0}", dbl)
    Next
    Console.WriteLine("----")
  End Sub

  '相対位置指定で index +2〜+5 の位置を切り出して返す
  Function Test2_Slice(a As ArraySegment(Of Double)) As ArraySegment(Of Double)
    Return New ArraySegment(Of Double)(a.Array, a.Offset + 2, 4)
  End Function

  '相対位置指定で index 0 と index 2 の値を入れ替える
  Sub Test3_Swap(a As ArraySegment(Of Double))
    Dim swap0 As Double = a(0)
    Dim swap2 As Double = a(2)
    a.Array(a.Offset + 0) = swap2
    a.Array(a.Offset + 2) = swap0
  End Sub

  '相対位置指定で index 1 の場所の値を 10 倍にする
  Sub Test4_Modify(a As ArraySegment(Of Double))
    a.Array(a.Offset + 1) *= 10
  End Sub
End Module
引用返信 編集キー/
■91907 / inTopicNo.5)  Re[4]: C++からVBへの変換
□投稿者/ えんえん (4回)-(2019/08/09(Fri) 12:28:51)
ありがとうございます。

C++でいう
&tmpr[r]
これって要素rだけを取り出しているのでしょうか?
あるいはr以降を取り出しているのでしょうか?
これをVBで書くとどうなりますか?
引用返信 編集キー/
■91908 / inTopicNo.6)  Re[5]: C++からVBへの変換
□投稿者/ Hongliang (854回)-(2019/08/09(Fri) 12:46:03)
> C++でいう
> &tmpr[r]
> これって要素rだけを取り出しているのでしょうか?
> あるいはr以降を取り出しているのでしょうか?

強いて言えば後者ですが、正しく言うとどちらでもありません。
ポインタをちゃんと説明するのは掲示板形式では割と無理目なので、C言語を勉強してください。

> これをVBで書くとどうなりますか?

VBでは言語レベルで表現することはできません。
// ライブラリなら、既に挙げられているArraySegmentやSpanが近いです。
引用返信 編集キー/
■91909 / inTopicNo.7)  Re[6]: C++からVBへの変換
□投稿者/ ぶなっぷ (204回)-(2019/08/09(Fri) 13:05:25)
微妙です。
要素r(言い方が微妙だが、まぁ気にしないでおく)の先頭ポインタです。

double* itemr = &tmpr[r];
としたとき。

*itemr は、まさに要素rの値(double型)

ただ、itemrはポインタであり、ポインタは配列アクセスができます。
なので、
  for(int i=0; i<...; i++)   // ループ終了条件は省略
  {
     itemr[i];
  }
とすれば、要素r以降の要素に順次アクセスすることもできます。

なお、ポインタはVBにはない概念です。
単純置換はできません。

引用返信 編集キー/
■91910 / inTopicNo.8)  Re[7]: C++からVBへの変換
□投稿者/ えんえん (6回)-(2019/08/09(Fri) 13:12:21)
ありがとうございます。

つまり、要素数r以降の配列を
別の配列にコピーすれば同じことができるわけでしょうか?

あと、IntPtrはポインタではないのでしょうか?


引用返信 編集キー/
■91911 / inTopicNo.9)  Re[8]: C++からVBへの変換
□投稿者/ Hongliang (855回)-(2019/08/09(Fri) 13:18:45)
2019/08/09(Fri) 13:19:21 編集(投稿者)

> つまり、要素数r以降の配列を
> 別の配列にコピーすれば同じことができるわけでしょうか?

「同じこと」はできませんが、近い処理はできるでしょう。
パフォーマンス的に凄い不利ですが。

> あと、IntPtrはポインタではないのでしょうか?

ポインタを格納できるサイズを持った整数型、です。
引用返信 編集キー/
■91912 / inTopicNo.10)  Re[9]: C++からVBへの変換
□投稿者/ えんえん (8回)-(2019/08/09(Fri) 13:23:41)
ありがとうございます。

For r As Integer = 0 To N - 1

Dim ar2 As New ArraySegment(Of Double)(ar, r, r)
Call FFT(・・・・,ar2,・・・・)

Next

は間違いで

For r As Integer = 0 To N - 1

Dim ar2 As New ArraySegment(Of Double)(ar, r, ar.length - r)
Call FFT(・・・・,ar2,・・・・)

Next

が正しいのでしょうか?
要素数を最後まで指定しないといけないですよね?

引用返信 編集キー/
■91913 / inTopicNo.11)  Re[8]: C++からVBへの変換
□投稿者/ 魔界の仮面弁士 (2292回)-(2019/08/09(Fri) 13:54:08)
2019/08/09(Fri) 17:05:54 編集(投稿者)

No91910 (えんえん さん) に返信
> つまり、要素数r以降の配列を
> 別の配列にコピーすれば同じことができるわけでしょうか?

配列をコピーする必要はありません。同じ配列を参照しておき、
その配列のどの位置を読み書きするのかという、Index 情報さえあれば OK です。

というか、今回の fft は配列引数の内容を書き換えるものなので、
配列の「コピー」を渡してしまっては、再帰処理の意味が無いと思います。



No91907 (えんえん さん) に返信
> C++でいう
> &tmpr[r]
> これって要素rだけを取り出しているのでしょうか?

実引数 &x[2] が 仮引数 y[] に渡されたとき、
y[1] は、呼び出し元の x[3] を意味します。


下記のようにすると、
 22, 33, 55
 11, 22, 44
と出力されます。

// ------------------
void fff(double ar[])
{
 std::cout << ar[-1] << ", " << ar[0] << ", " << ar[2] << std::endl;
}

int main()
{
 double ary[5];
 ary[0] = 11;
 ary[1] = 22;
 ary[2] = 33;
 ary[3] = 44;
 ary[4] = 55;

 fff(&ary[2]);
 fff(&ary[1]);

 return 0;
}


> あるいはr以降を取り出しているのでしょうか?

とりあえず、VB でこんな処理があったとします。
この場合、仮引数「d」は、実引数「dblArray(1)」への参照となっています。

 Sub Main()
  Dim dblArray As Double() = {11, 22, 33}
  Test(dblArray(1))
  Array.ForEach(dblArray, AddressOf Console.WriteLine)
 End Sub
 Sub Test(ByRef d As Double)
  d = 123
 End Sub


上記を実行すると、配列の内容が {11, 22, 33} から { 11, 123, 33 } に変化します。


Test 側で dblArray(1) だけでなく dblArray(2) も書き換えられるようにする場合には、
配列全体を渡すようにして、書き換える位置を別の引数で渡すようにします。

 Sub Main()
  Dim dblArray As Double() = {11, 22, 33}
  Test(dblArray, 1)
  Array.ForEach(dblArray, AddressOf Console.WriteLine)
 End Sub
 Sub Test(ByVal d As Double(), ByVal i As Integer)
  d(i + 0) = 123
  d(i + 1) = 456
 End Sub




> これをVBで書くとどうなりますか?

たとえば上記の処理は、最近の C# だとこのように書けます。

/* 要 C# 7.2 以上 / VS2017 15.5 以上 */
static void Main()
{
  double[] dblArray = { 11, 22, 33 };
  Test(dblArray.AsSpan(1)); // { 22, 33 } が渡される
  Array.ForEach(dblArray, Console.WriteLine); // { 11, 123, 456 } が渡される
}

private static void Test(Span<double> span)
{
  span[0] = 123;
  span[1] = 456;
}


将来的には Visual Basic でも同じように書ける様になると思いますが、
現状の言語仕様だと、自前で位置管理の変数を追加するなり、
ArraySegment を経由させるなり、あるいは IntPtr で
アンマネージ管理する力技ぐらいしか手が無いかと思います。

今回のように再帰呼び出しなら、クロージャにまとめてしまっても良いかも。
引用返信 編集キー/
■91917 / inTopicNo.12)  Re[10]: C++からVBへの変換
□投稿者/ 魔界の仮面弁士 (2293回)-(2019/08/09(Fri) 17:42:02)
2019/08/13(Tue) 00:01:21 編集(投稿者)

追記:拡張メソッドの不具合修正

No91912 (えんえん さん) に返信
> Dim ar2 As New ArraySegment(Of Double)(ar, r, ar.length - r)
> が正しいのでしょうか?
> 要素数を最後まで指定しないといけないですよね?

そうなりますね。


処理を分かりやすくするために、ArraySegment のための
拡張メソッドを用意しておくのが良いでしょう。


後述のコードを用意しておけば、
  // 元の C++ 実装
  tmpr[m * n_radix + j] = xr * wr - xi * wi;
に相当する処理を、
  tmpr.SetValue(m * n_radix + j, xr * wr - xi * wi)
の構文で書ける様になります。


また、再帰呼び出しの
  // C++
  fft(n_radix, theta * radix, &tmpr[r], &tmpi[r], ar, ai);
についても、
  fft(n_radix, theta * radix, tmpi.AsSegment(r), tmpi.AsSegment(r), ar, ai)
で済むようになります。



'----------------------
Module ArraySegmentExtension
  <System.Runtime.CompilerServices.Extension>
  Public Function AsSegment(Of T)(this As T()) As ArraySegment(Of T)
    Return New ArraySegment(Of T)(this)
  End Function

  <System.Runtime.CompilerServices.Extension>
  Public Function AsSegment(Of T)(this As T(), start As Integer) As ArraySegment(Of T)
    Return New ArraySegment(Of T)(this, start, this.Length - start)
  End Function

  <System.Runtime.CompilerServices.Extension>
  Public Function AsSegment(Of T)(this As ArraySegment(Of T), start As Integer) As ArraySegment(Of T)
    'Return New ArraySegment(Of T)(this.Array, this.Offset + start, this.ToArray.Length - this.Offset - start)
    Return New ArraySegment(Of T)(this.Array, this.Offset + start, this.Array.Length - this.Offset - start)
  End Function

  <System.Runtime.CompilerServices.Extension>
  Public Function AsSegment(Of T)(this As ArraySegment(Of T), start As Integer, length As Integer) As ArraySegment(Of T)
    Return New ArraySegment(Of T)(this.Array, this.Offset + start, length)
  End Function

  <System.Runtime.CompilerServices.Extension>
  Public Function AsSegment(Of T)(this As T(), start As Integer, length As Integer) As ArraySegment(Of T)
    Return New ArraySegment(Of T)(this, start, length)
  End Function

  <System.Runtime.CompilerServices.Extension>
  Public Function SetValue(Of T)(this As ArraySegment(Of T), index As Integer, newValue As T) As ArraySegment(Of T)
    this.Array(this.Offset + index) = newValue
    Return this
  'End Sub
  End Function
End Module
引用返信 編集キー/
■91924 / inTopicNo.13)  Re[9]: C++からVBへの変換
□投稿者/ えんえん (10回)-(2019/08/10(Sat) 07:13:12)
ありがとうございます。


void fft(int n, double theta, double ar[], double ai[], 
        double tmpr[], double tmpi[])
{
    int radix, n_radix, j, m, r;
    double xr, xi, wr, wi;

    if (n <= 1) return;
    /* ---- factorization ---- */
    for (radix = 2; radix * radix <= n; radix++) {
        if (n % radix == 0) break;
    }
    if (n % radix != 0) radix = n;
    n_radix = n / radix;
    /* ---- butterflies ---- */
    for (j = 0; j < n_radix; j++) {
        for (m = 0; m < radix; m++) {
            xr = ar[j];
            xi = ai[j];
            for (r = n_radix; r < n; r += n_radix) {
                wr = cos(theta * m * r);
                wi = sin(theta * m * r);
                xr += wr * ar[r + j] - wi * ai[r + j];
                xi += wr * ai[r + j] + wi * ar[r + j];
            }
            wr = cos(theta * m * j);
            wi = sin(theta * m * j);
            tmpr[m * n_radix + j] = xr * wr - xi * wi;
            tmpi[m * n_radix + j] = xi * wr + xr * wi;
        }
    }
    for (r = 0; r < n; r += n_radix) {
        fft(n_radix, theta * radix, &tmpr[r], &tmpi[r], ar, ai);
    }
    for (j = 0; j < n_radix; j++) {
        for (m = 0; m < radix; m++) {
            ar[radix * j + m] = tmpr[n_radix * m + j];
            ai[radix * j + m] = tmpi[n_radix * m + j];
        }
    }
}


これを以下のように翻訳してみました。


    Public Sub one_D_FFT(ByRef xr#(), ByRef xi#())


        Dim n As Integer = xr.GetLength(0) - 1

        Dim theta = 2 * Math.PI / n

        Dim tmpr(n) As Double, tmpi(n) As Double

        Call fft(n, theta, xr, xi, tmpr, tmpi, 0)


    End Sub

    Private Sub fft(ByVal n%, ByVal theta#, ByRef ar#(), ByRef ai#(), ByRef tmpr#(), ByRef tmpi#(), ByVal r_st%)

        If n <= 1 Then Exit Sub

        Dim radix As Integer = 2
        Do While radix * radix <= n
            If n Mod radix = 0 Then Exit Do
            radix += 1
        Loop

        If n Mod radix <> 0 Then radix = n

        Dim n_radix As Integer = n \ radix

        For j As Integer = 0 To n_radix - 1

            For m As Integer = 0 To radix - 1

                Dim xr As Double = ar(r_st + j)
                Dim xi As Double = ai(r_st + j)

                For r As Integer = n_radix To n - 1 Step n_radix

                    Dim wr0 As Double = Math.Cos(theta * m * r)
                    Dim wi0 As Double = Math.Sin(theta * m * r)
                    xr += (wr0 * ar(r_st + r + j) - wi0 * ai(r_st + r + j))
                    xi += (wr0 * ai(r_st + r + j) + wi0 * ar(r_st + r + j))

                Next r


                Dim wr As Double = Math.Cos(theta * m * j)
                Dim wi As Double = Math.Sin(theta * m * j)
                tmpr(m * n_radix + j) = xr * wr - xi * wi
                tmpi(m * n_radix + j) = xi * wr + xr * wi

            Next m

        Next j

        For r As Integer = 0 To n - 1 Step n_radix

            fft(n_radix, theta * radix, tmpr, tmpi, ar, ai, r)

        Next r



        For j As Integer = 0 To n_radix - 1

            For m As Integer = 0 To radix - 1

                ar(r_st + radix * j + m) = tmpr(n_radix * m + j)
                ai(r_st + radix * j + m) = tmpi(n_radix * m + j)

            Next m

        Next j


    End Sub



しかし、むちゃくちゃな結果しか得られないのですが・・・・。
どこが間違っていますでしょうか?


引用返信 編集キー/
■91925 / inTopicNo.14)  Re[10]: C++からVBへの変換
□投稿者/ 魔界の仮面弁士 (2294回)-(2019/08/10(Sat) 09:41:30)
No91924 (えんえん さん) に返信
> Public Sub one_D_FFT(ByRef xr#(), ByRef xi#())
> Private Sub fft(ByVal n%, ByVal theta#, ByRef ar#(), ByRef ai#(), ByRef tmpr#(), ByRef tmpi#(), ByVal r_st%)

r_st% で、添字のオフセットを表す方向で実装しようしているのですね。

ところで何故、配列を ByRef で渡しているのでしょうか?
VBA じゃあるまいし…。

OutAttribute 属性的な意味合いがある場合には ByRef にすることもありますが、
配列は「参照型」なのですから、通常は ByRef にする必要などないはずです。

Return をあえて Exit Sub の構文で記述している点を鑑みると、
.NET ではない時代の Visual Basic に慣れている方なのでしょうか。



> どこが間違っていますでしょうか?

最大の問題はここです。r_st が考慮されていませんよね。
この処理が再帰であることを忘れていませんか?

> for (r = 0; r < n; r += n_radix) {
>   fft(n_radix, theta * radix, &tmpr[r], &tmpi[r], ar, ai);
> }
******
> For r As Integer = 0 To n - 1 Step n_radix
>   fft(n_radix, theta * radix, tmpr, tmpi, ar, ai, r)
> Next r
引用返信 編集キー/
■91927 / inTopicNo.15)  Re[11]: C++からVBへの変換
□投稿者/ えんえん (11回)-(2019/08/10(Sat) 10:16:21)
ありがとうございます。


        For r As Integer = 0 To n - 1 Step n_radix

            fft(n_radix, theta * radix, tmpr, tmpi, ar, ai, r_st + r)

        Next r

としてみましたが、
それでも、やはり正しい結果は得られないのですが、なぜでしょうか?


配列はByValやByRefを指定しなくとも
参照型(ByRef)で渡されることは知っていますが
後から、コードを見たときに分かりやすくするために
一応、付けています。

ちなみに、普通は配列にはByValやByRefを付けないものなのでしょうか?

ReturnはFunctionのためのもので、
Exit Sub はSubのためのものだと思っていました。

仰る通り、長い間VBAを使ってきましたので、
その名残が残っています。



引用返信 編集キー/
■91930 / inTopicNo.16)  Re[12]: C++からVBへの変換
□投稿者/ 魔界の仮面弁士 (2295回)-(2019/08/10(Sat) 14:19:15)
No91927 (えんえん さん) に返信
> それでも、やはり正しい結果は得られないのですが、なぜでしょうか?

「ar と ai」「tmpr と tmpi」は、再帰処理のたびに入れ替わりますよね。
ざっと見た感じでは、入れ替えた際の添字管理に問題があるように見えます。


話を単純化するため、元の処理を下記のように置き換えてみましょう。

 void Proc(int x[], int y[])
 {
  if (x[0] != 0) Proc(&y[1], x);
 }
 void main()
 {
  int ar1[4] = { 11, 22, 33, 0 };
  int ar2[4] = { 55, 66, 77, 0 };
  Proc(ar1, ar2);
 }

この場合、ポインタは以下のように遷移しますよね。
この動作をポインタ代わりの r_st% で管理しようとして、失敗しているように見えます。

 main から渡す実引数は
  ar1 が { 11, 22, 33, 0 }
  ar2 が { 55, 66, 77, 0 }
 → C++ の main から呼び出された Proc の仮引数
  x は { 11, 22, 33, 0 }
  y は { 55, 66, 77, 0 }
 →→→ 1 回目の再帰処理で呼ばれた Proc の仮引数
  x は { 66, 77, 0 }
  y は { 11, 22, 33, 0 }
 →→→→ 2 回目の再帰処理で呼ばれた Proc の仮引数
  x は { 22, 33, 0 }
  y は { 66, 77, 0 }
 →→→→→ 3 回目の再帰処理で呼ばれた Proc の仮引数
  x は { 77, 0 }
  y は { 22, 33, 0 }
 →→→→→→ 4 回目の再帰処理で呼ばれた Proc の仮引数
  x は { 33, 0 }
  y は { 77, 0 }
 →→→→→→→ 5 回目の再帰処理で呼ばれた Proc の仮引数
  x は { 0 }
  y は { 33, 0 }
 ←←←←←← 再帰が終わって呼び出し元(4 回目)に戻る
  x は { 33, 0 }
  y は { 77, 0 }
 ←←←←← 再帰が終わって呼び出し元(3 回目)に戻る
  x は { 77, 0 }
  y は { 22, 33, 0 }
 ←←←← 再帰が終わって呼び出し元(2 回目)に戻る
  x は { 22, 33, 0 }
  y は { 66, 77, 0 }
 ←←← 再帰が終わって呼び出し元(1 回目)に戻る
  x は { 66, 77, 0 }
  y は { 11, 22, 33, 0 }
 ←← 再帰が終わって最初の呼び出しに戻る
  x は { 11, 22, 33, 0 }
  y は { 55, 66, 77, 0 }
 ← C++ の main まで戻ってきて
  ar1 が { 11, 22, 33, 0 }
  ar2 が { 55, 66, 77, 0 }



> 配列はByValやByRefを指定しなくとも
> 参照型(ByRef)で渡されることは知っていますが

いろいろごちゃ混ぜになっているような。

ByVal / ByRef の省略の話と、配列であるかどうかは無関係ですし、
ByVal / ByRef の話と 値型 / 参照型 の話も別物ですよ。


> 後から、コードを見たときに分かりやすくするために
> 一応、付けています。

『値渡し』を望むなら、ByVal は省略しようと明示しようと、どちらでも構わないと思います。
自分の場合、新規開発ならば省略表記にした方がスッキリするので記述しませんが、
VBA からの移行であれば、明示的に記述して移植時の意図を明確にするようにしています。

一方、『参照渡し』を望むなら、VBA とは違って、ByRef を明示する必要があります。
しかし ByRef を付けた理由が「配列だから」というのなら、使い方が間違っています。


> ちなみに、普通は配列にはByValやByRefを付けないものなのでしょうか?
配列かどうかは一切関係ありません。

使い分けの方針としては、「出力引数」として実装する場合に限って ByRef を使い、
それ以外の場合は全て、値渡しとして処理させるのが普通です。

ただ、出力引数を使うことがそもそも稀であるため、.NET Framework のライブラリで見ても、
ByRef が利用されているものは殆どありません。使うとしても TryParse メソッドぐらいのものでしょう。
引用返信 編集キー/
■91932 / inTopicNo.17)  Re[13]: C++からVBへの変換
□投稿者/ 774RR (727回)-(2019/08/10(Sat) 15:02:41)
全然どうでもいいけど

オイラが C++ と C# で同じ処理をさせたら性能が団地だった例
http://bbs.wankuma.com/index.cgi?mode=al2&namber=78124&KLOG=132

FFT なんぞテキトーに探せばいくらでもライブラリが転がっているような気のせいがする。
わざわざ詳しくない言語で書かれているプログラムを移植するまでもない。

引用返信 編集キー/
■91933 / inTopicNo.18)  Re[14]: C++からVBへの変換
□投稿者/ 魔界の仮面弁士 (2296回)-(2019/08/10(Sat) 16:23:56)
2019/08/10(Sat) 16:26:50 編集(投稿者)

No91932 (774RR さん) に返信
> FFT なんぞテキトーに探せばいくらでもライブラリが転がっているような気のせいがする。

この辺にありそうな気がします。確認はしていませんけど。
https://www.nuget.org/packages?q=fft

VB.NET に移植済みの実装が欲しいという話なら、こんなのもあります。
http://numeric.world.coocan.jp/computer/vb/mathpack.htm

Excel VBA でも良ければこんなのもありました。
関数化はされていないものの、再帰処理を用いないフラットな実装。
http://tsuyu.cocolog-nifty.com/blog/2007/03/publi.html


真面目に再実装する気があるなら、複素数構造体を併用すると良いかもしれません。
https://docs.microsoft.com/ja-jp/dotnet/api/system.numerics.complex.frompolarcoordinates

複素数構造体を用いた実装例は下記にありました。
F# なので参考にはしにくいかもしれませんが。
http://www.fssnip.net/dC/title/fast-Fourier-transforms-FFT-



No91927 (えんえん さん) に返信
> For r As Integer = 0 To n - 1 Step n_radix
>   fft(n_radix, theta * radix, tmpr, tmpi, ar, ai, r_st + r)
> Next r
> としてみましたが、
> それでも、やはり正しい結果は得られないのですが、なぜでしょうか?

オリジナル版にある
 fft(n_radix, theta * radix, &tmpr[r], &tmpi[r], ar, ai);
ってのは
 fft(n_radix, theta * radix, &tmpr[r], &tmpi[r], &ar[0], &ai[0]);
でもあるわけですし、再帰で tmpX と aX が入れ替わる点が肝かと思います。

すなわち、index の offset は r_st 一つでは足りなくて、
a 系と tmp 系の両方に対して用意すべきかと思います。
引用返信 編集キー/
■91934 / inTopicNo.19)  Re[15]: C++からVBへの変換
□投稿者/ えんえん (13回)-(2019/08/11(Sun) 21:42:09)
ありがとうございます。


二つ変数を使った方法はどうやれば良いか分からなかったので
とりあえず、先に ArraySegmentを使った方法を試してみました。





    Public Sub one_D_FFT(ByRef xr#(), ByRef xi#())


        Dim n As Integer = xr.GetLength(0) - 1

        Dim theta = 2 * Math.PI / n

        Dim xr2 As New ArraySegment(Of Double)(xr), xi2 As New ArraySegment(Of Double)(xi)

        Dim tmpr(n) As Double, tmpi(n) As Double
        Dim tmpr2 As New ArraySegment(Of Double)(tmpr), tmpi2 As New ArraySegment(Of Double)(tmpi)

        Call fft(n, theta, xr2, xi2, tmpr2, tmpi2)


    End Sub




    Private Sub fft(ByVal n%, ByVal theta#, ByRef ar As ArraySegment(Of Double), ByRef ai As ArraySegment(Of Double), ByRef tmpr As ArraySegment(Of Double), ByRef tmpi As ArraySegment(Of Double))

        If n <= 1 Then Exit Sub

        Dim radix As Integer = 2
        Do While radix * radix <= n
            If n Mod radix = 0 Then Exit Do
            radix += 1
        Loop

        If n Mod radix <> 0 Then radix = n

        Dim n_radix As Integer = n \ radix

        For j As Integer = 0 To n_radix - 1

            For m As Integer = 0 To radix - 1

                Dim xr As Double = ar.Array(j)
                Dim xi As Double = ai.Array(j)

                For r As Integer = n_radix To n - 1 Step n_radix

                    Dim wr0 As Double = Math.Cos(theta * m * r)
                    Dim wi0 As Double = Math.Sin(theta * m * r)
                    xr += (wr0 * ar.Array(r + j) - wi0 * ai.Array(r + j))
                    xi += (wr0 * ai.Array(r + j) + wi0 * ar.Array(r + j))

                Next r


                Dim wr As Double = Math.Cos(theta * m * j)
                Dim wi As Double = Math.Sin(theta * m * j)
                tmpr.Array(m * n_radix + j) = xr * wr - xi * wi
                tmpi.Array(m * n_radix + j) = xi * wr + xr * wi

            Next m

        Next j

        For r As Integer = 0 To n - 1 Step n_radix

            fft(n_radix, theta * radix, New ArraySegment(Of Double)(tmpr.Array, tmpr.Offset + r, tmpr.Count - r), New ArraySegment(Of Double)(tmpi.Array, tmpi.Offset + r, tmpi.Count - r), ar, ai)

        Next r



        For j As Integer = 0 To n_radix - 1

            For m As Integer = 0 To radix - 1

                ar.Array(radix * j + m) = tmpr.Array(n_radix * m + j)
                ai.Array(radix * j + m) = tmpi.Array(n_radix * m + j)

            Next m

        Next j


    End Sub


しかし、真っ白なデータしか出力されないのですが。。。。
何が間違っていますでしょうか?





引用返信 編集キー/
■91938 / inTopicNo.20)  Re[16]: C++からVBへの変換
 
□投稿者/ 魔界の仮面弁士 (2297回)-(2019/08/12(Mon) 12:05:54)
2019/08/12(Mon) 13:21:00 編集(投稿者)

No91934 (えんえん さん) に返信
> 何が間違っていますでしょうか?

『ステップ実行』しましょう。

ループ処理などを一行ずつ実行してみた上で、

  この場所では、この変数に
  こういう値がセットされるはずなのに、
  実際にはこんな値になってしまった
 」
…という点を追跡できれば、どこの翻訳実装に問題があったことが明確になりますよね。


> とりあえず、先に ArraySegmentを使った方法を試してみました。

何か所か間違いがありますが、まずはここ。
 tmpr[m * n_radix + j] = xr * wr - xi * wi;

No91934 のコードでは、上記を
> tmpr.Array(m * n_radix + j) = xr * wr - xi * wi
と翻訳してしまっていますので、ここが大きな間違いです。

たとえば、
 Dim x() As Byte = New Byte(5) {11, 22, 33, 44, 55, 66}
 Dim y As New ArraySegment(Of Byte)(x)
 Dim z As New ArraySegment(Of Byte)(x, 2, 3)
において、「y.Array」や「z.Array」は、配列 x への参照を意味します。
※ Span も似たようなものですが、Span はさらに各要素への参照も持ちます。

なので、x(3) = 100 と代入すれば、z(1) も同様に 44 から 100 へと変化します。

この時、それぞれの要素を列挙すれば
 x は {11, 22, 33, 100, 55, 66} 相当 (y.Array や z.Array も同様)
 y は {11, 22, 33, 100, 55, 66} 相当 (y.Offset は 0、y.Count は 6)
 z は {33, 100, 55} 相当 (z.Offset は 2、z.Count は 3)
となります。
Array / Offset / Count プロパティの 3 セットが、この構造体の肝です。
ここまでは良いでしょうか。


ArraySegment 構造体の場合、値の取得については
 MsgBox( z(i) )
のように書けるのですが、問題は書き込みです。インデクサが ReadOnly なので、
 z(i) = newValue
のようには書けません。(Span 構造体なら可能になるはず)

そのため、オフセット指定の書き込みが必要な場合には、
 z.Array(z.Offset + i) = newValue
のように、Array プロパティと Offset プロパティを併用することになります。
No91934 のコードでは、この点が考慮されていないように見受けられます。


=====

それと、むやみに ByRef を使わないでください。
今回は出力引数が存在しないため、ByRef の出番は一切ありません。

先にも述べましたが、出力引数が必要な場合以外は、値渡しで実装するべきです。



Sub Main()
  Dim x() As Byte = New Byte(3) {&H11, &H22, &HAB, &HCD}
  MsgBox(BitConverter.ToString(x))

  'AsSegment については No91917 を参照
  Test(x.AsSegment())
  MsgBox(BitConverter.ToString(x))

  Test(x.AsSegment(2, 2))
  MsgBox(BitConverter.ToString(x))
End Sub

' a(0) と a(1) の中身を入れ替える処理
' この時、引数 ArraySegment 構造体を ByRef にする必要は無い!
Sub Test(Of T)(ByVal a As ArraySegment(Of T))
  Dim swap() As T = {a(0), a(1)}
  a.Array(a.Offset + 1) = swap(0)
  a.Array(a.Offset + 0) = swap(1)
End Sub


※ 先の No91917 ですが、最後の End Function が誤って End Sub になっていました。
 恐れ入りますが、 End Function に読み替えておいてください。


> Public Sub one_D_FFT(ByRef xr#(), ByRef xi#())
> Private Sub fft(ByVal n%, ByVal theta#, ByRef ar As ArraySegment(Of Double), ByRef ai As ArraySegment(Of Double), ByRef tmpr As ArraySegment(Of Double), ByRef tmpi As ArraySegment(Of Double))

これらについて、メソッド名を分ける必要は無いと思います。オーバーロードで十分ではないでしょうか。
 Public Sub Fft(xr#(), xi#())
 Private Sub Fft(n%, theta#, ar As ArraySegment(Of Double), ar As ArraySegment(Of Double), tmpr As ArraySegment(Of Double), tmpi As ArraySegment(Of Double))


メソッド名を分けるにしても、両者でメソッド名の大文字小文字の使い分けが不揃いな点が気にかかります。
.NET Framework においては一般的に、
  ・クラス名やメソッド名は PascalCase 構文の大文字小文字表記とする。
  ・変数や引数は、camelCase 構文の大文字小文字表記とする。
というのが一般的ですので、この点を意識したコーディングにされることをお奨めします。
https://docs.microsoft.com/ja-jp/dotnet/standard/design-guidelines/naming-guidelines
http://objectclub.jp/community/codingstandard/CodingStdVB.doc
引用返信 編集キー/

次の20件>
トピック内ページ移動 / << 0 | 1 >>

管理者用

- Child Tree -