C#ATIA

↑タイトル詐欺 主にFusion360API 偶にCATIA V5 VBA(絶賛ネタ切れ中)

VBAのリスト問題3

こちらの続きです。
VBAのリスト問題2 - C#ATIA

あぁ配列試す前に、書くべきでした・・・・。
ところで、お気付きでしょうか?(何が?)


コレクションの結果(やる度に結果は変わります)のLong型を
扱ったものですが、

**コレクション-キー無し**
Long 代入:0.0390625s
呼出:24.4453125s

**コレクション-キー付き**
Long 代入:0.890625s
呼出:0.3671875s

キー無しは代入に比べ、呼び出しが非常に遅いのですが、
キー付きは呼び出しの方が、代入より速いです。
逆転しています。

ここからは想像です!
何処かにメモリ確保の仕方とかの説明があれば良いのですが、
見つけられませんでした。(きっと何処かには有ると思います)

僕の中ではキー無しの場合は、こんな感じのイメージを持っています。

んーセンス無いな。 まばらに描いているのはメモリ上のアチコチに
散らばっているイメージです。
各Itemは、データの中身(本当は中身のアドレスだと思う)と次の
インデックスがセットになって情報を持っているような気がします。

キー付きの場合は、こんなイメージです。

各データはバラバラに配置されているのですが、キーについては
別の所に連続しておいてあるような気がします。

では確認。

こんな感じでサンプルを作りました。
もう、小出しでは無くドバっと。

'vba

Option Explicit

Private sw_ As StopWatch

Sub collection_key_test()

    Dim Count As Long
    Count = 30000
    
    'コレクション作成
    Dim lst As Collection
    Set lst = create_collection(Count, False)
    
    'キー無し
    Set sw_ = New StopWatch
    sw_.start
    Debug.Print "--キー無し--"
    
    Call non_key_test(lst)
    Call sw_.total("Total" & ":")

    'コレクション作成
    Set lst = create_collection(Count, True)

    'キー付き
    sw_.start
    Debug.Print "--キー付き--"
    
    Call use_key_test(lst)
    Call sw_.total("Total" & ":")
    
End Sub


Private Sub use_key_test( _
    ByVal lst As Collection)
    
    Dim i As Long, v As Long
    For i = 0 To lst.Count - 1
        v = lst.Item(CStr(i))
        If i Mod 1000 = 0 Then
            Call sw_.split(CStr(i) & ":")
        End If
    Next
    
End Sub


Private Sub non_key_test( _
    ByVal lst As Collection)
    
    Dim i As Long, v As Long
    For i = 1 To lst.Count
        v = lst(i)
        If i Mod 1000 = 0 Then
            Call sw_.split(CStr(i) & ":")
        End If
    Next
    
End Sub

それぞれ30000回Forを行い、1000回毎に時間を書き出します。

比較しやすいように、した結果がこちらです。

キー付きは最初から最後まで、ほぼ一定の時間で読み出せていますが、
キー無しは後半になる程、遅くなっています。
(For Eachも最初から最後まで、ほぼ一定でした)

この結果からすると、上で示した画像のイメージに近い事が分かります。
つまり30000個目にアクセスする為には、29999個目まで辿らないと
30000個目を見つける事が出来ない と言う事です。

じゃぁ試しに、step -1でforしたらどうなるのか?
non_key_test関数のみを書き換えます。

Private Sub non_key_test( _
    ByVal lst As Collection)
    
    Dim i As Long, v As Long
'    For i = 1 To lst.Count
    For i = lst.Count To 1 Step -1
        v = lst(i)
        If i Mod 1000 = 0 Then
            Call sw_.split(CStr(i) & ":")
        End If
    Next
    
End Sub

実行結果はこんな感じです。

--キー無し--
30000:0.014892578125s
29000:0.656005859375s
28000:0.630126953125s
27000:0.629150390625s
26000:0.590087890625s
25000:0.56396484375s
・・・
6000:0.14111328125s
5000:0.113037109375s
4000:0.090087890625s
3000:0.06884765625s
2000:0.049072265625s
1000:0.030029296875s
Total:10.10498046875s

Forの後半は、前半より早くなってきています。
恐らくイメージ通りです。

じゃ、For EachがIndexが大きくなっても、遅くならないのか?

Forの場合、ここでListの要素が代入されています。

lstにとって、インデックス "i" が幾つが来るのかが分かっていない
のだろうと思います。

それに対しFor Eachは、ここで直接中身が取り出されています。

その為、次の要素が何かが分かっているような気がします。
(それは、イテレータだと思う)

・・・眠い。

VBAのリスト問題2

こちらの続きです。
VBAのリスト問題1 - C#ATIA

続いては固定長配列です。もう、やらなくてもこれが一番速いのが
分かっているのですが、指標的な意味合いです。

Private Sub test3( _
    ByVal Count As Long)

    Dim ary(50000) As Long
    
    Dim i As Long
    For i = 0 To Count - 1
        ary(i) = i
    Next
    
    sw_.split "Long 代入:"
    
    Dim x As Long
    For i = 0 To UBound(ary)
        x = ary(i)
    Next

    sw_.split "呼出:"

    
    sw_.split "--:"
    Dim objAry(50000) As myObj
    Dim obj As myObj
    Set obj = New myObj
    
    For i = 0 To Count
        Set objAry(i) = obj
    Next
    
    sw_.split "Object 代入:"
    
    For i = 0 To UBound(ary)
        Set obj = objAry(i)
    Next
    
    sw_.split "呼出:"

End Sub

結果はこちら。

**固定長配列**
Long 代入:0.0078125s
呼出:0s
--:0s
Object 代入:0.0078125s
呼出:0.0078125s
**終了** :0.046875s

2倍とか3倍とかでは無く、2桁ぐらい違いますね。
但し、コードを書く時点で必要最大サイズが分かる場面はほぼ無く、
不必要なサイズの配列用意するのも、ちょっと違うような
気もしてます。正直な所、使い道としては無いですよね。


続いては動的配列です。使用する場面の多さから、これを”配列”と
呼びたいところです。

Private Sub test4( _
    ByVal Count As Long)

    Dim ary() As Long
    ReDim ary(Count)
    
    Dim i As Long
    For i = 0 To Count - 1
        ary(i) = i
    Next
    
    sw_.split "Long 代入:"
    
    Dim x As Long
    For i = 0 To UBound(ary)
        x = ary(i)
    Next

    sw_.split "呼出:"

    
    sw_.split "--:"
    Dim objAry() As myObj
    ReDim objAry(Count)
    
    Dim obj As myObj
    Set obj = New myObj
    
    For i = 0 To Count
        Set objAry(i) = obj
    Next
    
    sw_.split "Object 代入:"
    
    For i = 0 To UBound(ary)
        Set obj = objAry(i)
    Next
    
    sw_.split "呼出:"

End Sub

結果の一例です。

**可変長配列**
Long 代入:0.015625s
呼出:0.0078125s
--:0s
Object 代入:0s
呼出:0.015625s
**終了** :0.0390625s

ちょっと驚いたのは、オブジェクトの代入が固定長配列より
速かった事です。何でだろう?


次は同じ動的配列なのですが、最初にサイズが決められない
ような状態の場合、単なるReDimでは無く、ReDim Preserveで
サイズを大きくする方法があります。
このPreserveを使った極端な例として、代入を行う直前に
1個づつサイズを広げ代入するようにしたものです。

Private Sub test5( _
    ByVal Count As Long)

    Dim ary() As Long
    
    Dim i As Long
    For i = 0 To Count - 1
        ReDim Preserve ary(i)
        ary(i) = i
    Next
    
    sw_.split "Long 代入:"
    
    Dim x As Long
    For i = 0 To UBound(ary)
        x = ary(i)
    Next

    sw_.split "呼出:"

    
    sw_.split "--:"
    Dim objAry() As myObj
    
    Dim obj As myObj
    Set obj = New myObj
    
    For i = 0 To Count
        ReDim Preserve objAry(i)
        Set objAry(i) = obj
    Next
    
    sw_.split "Object 代入:"
    
    For i = 0 To UBound(objAry)
        Set obj = objAry(i)
    Next
    
    sw_.split "呼出:"

End Sub

結果の一例です。

**可変長配列 Preserve**
Long 代入:0.03125s
呼出:0s
--:0s
Object 代入:0.0390625s
呼出:0.015625s
**終了** :0.109375s

思ったより、差が出なかった・・・。
ループさせる回数が少ないからだろうと思います。

と言う事で、動的配列の2種類を50000->500000に
増やした結果の一例がこちら。

**可変長配列**
Long 代入:0.0234375s
呼出:0.0234375s
--:0s
Object 代入:0.046875s
呼出:0.0546875s
**終了** :0.1640625s

**可変長配列 Preserve**
Long 代入:0.9375s
呼出:0.0234375s
--:0s
Object 代入:0.890625s
呼出:0.046875s
**終了** :1.9296875s

呼び出しについては、まぁ同じ処理なので誤差です。
代入については結構な差が付きました。
理由が分かっているのですが、他を試した際に書こうかな?と
思ってます。

VBAのリスト問題1

"VBAのリスト"としましたが、使うなら配列か?コレクションか?
と言う事です。
多くの先人の方々が、実測し答えも出ているような気もしますが、
githubで公開されているものや自作も含め、そろそろ自分の中での
答えを出したい気持ちがあります。
(配列とコレクションであれば、一時的にコレクションを使って
最終的には動的配列を使う が僕の中の答えです)

まず、処理時間を測定する為のStopWatchクラスです。

'vba StopWatch.cls

Option Explicit

Private startTime As Double
Private splitTime As Double


Private Sub Class_Initialize()
    startTime = -1
    splitTime = -1
End Sub


Public Sub start()
    startTime = Timer
    splitTime = startTime
End Sub


Public Function total( _
    Optional ByVal msg As String = "") As Double
    
    Dim t As Double
    t = Timer - startTime
    total = t
    
    Call dump_time(t, msg)
End Function


Public Function split(Optional ByVal msg As String = "")
    Dim t As Double
    t = Timer - splitTime
    split = t
    
    Call dump_time(t, msg)
    splitTime = Timer
End Function


Private Function is_started() As Boolean
    is_started = IIf(startTime > 0, True, False)
End Function


Private Sub dump_time( _
    ByVal timeVal As Double, _
    Optional ByVal msg As String = "")

    If Not is_started Then
        dump "---"
        Exit Sub
    End If
    
    dump msg & timeVal & "s"
End Sub


Private Sub dump(ByVal msg As String)
    Debug.Print msg
End Sub

startメソッドで測定開始し、totalでstartからの時間を出力します。
splitは前回にsplitからの時間を出力し、最初のsplitの時はstartからの
時間の出力です。
resetを用意すべきかどうか悩みましたが、もう一度インスタンス
すれば済む話なので、ありません。

念の為Long型とObject型の両方を試しておきたい為、処理で
利用するだけの無意味なクラスです。

'vba myObj.cls

Option Explicit

Public x As Long

何も無いです。ほんの気持ちです。

続いてエントリーポイントとなるメインの関数(プロシージャ?)です。

'vba main.bas

Option Explicit

Dim sw_ As StopWatch

Sub test_list()

    Dim Count As Long
    Count = 50000
    
    Set sw_ = New StopWatch

    Debug.Print vbCrLf & "**コレクション**"
    sw_.start
    Call test1(Count)
    sw_.total "**終了** :"

End Sub

"Call test1(Count)"を書き換える、又は追加しながら実行します。


それでは、テストする関数です。
まずは無難に普通のコレクションにします。

Private Sub test1( _
    ByVal Count As Long)
    
    Dim lst As Collection
    Set lst = New Collection
    
    Dim i As Long
    For i = 0 To Count
        lst.Add i
    Next
    
    sw_.split "Long 代入:"
    
    Dim x As Long
    For i = 1 To lst.Count
        x = lst(i)
    Next
    sw_.split "呼出:"

    
    sw_.split "--:"
    Set lst = New Collection
    Dim obj As myObj
    Set obj = New myObj
    
    For i = 0 To Count
        lst.Add obj
    Next
    
    sw_.split "Object 代入:"
    
    For i = 1 To lst.Count
        Set obj = lst.Item(i)
    Next
    
    sw_.split "呼出:"

End Sub

"for eachの方が早いじゃん"と言われるのは十分承知していますが、
統一してforで処理します。
処理的には、Long型の代入と取り出しを行い、続いてオブジェクト型の
代入と取り出しを行います。実際に実行した結果の一例です。

**コレクション**
Long 代入:0.0390625s
呼出:24.4453125s
--:0s
Object 代入:0.0703125s
呼出:24.390625s
**終了** :48.96875s

LongとObjectに大きな差はありませんね。
代入に比べ呼び出しは多くの時間がかかる事がわかりました。
"コレクションは遅い"と言われる理由は、呼び出しが遅い事が
原因の様に感じます。

続いてキー付きのコレクションです。実はこの機能はかなり
後になって知りました。要はdictionary型のような扱いが
出来るようですが、代入のaddメソッドのkeyとvalue
他の多くの言語と逆です・・・。(気持ちはわかる)
癖強めですが、処理が早いと言う事は聞いたことがあります。

Private Sub test2( _
    ByVal Count As Long)

    Dim lst As Collection
    Set lst = New Collection
    
    Dim i As Long
    For i = 0 To Count
        lst.Add i, CStr(i)
    Next
    
    sw_.split "Long 代入:"
    
    Dim x As Variant
    For i = 0 To lst.Count - 1
        x = lst.Item(CStr(i))
    Next

    sw_.split "呼出:"

    
    sw_.split "--:"
    Set lst = New Collection
    Dim obj As myObj
    Set obj = New myObj
    
    For i = 0 To Count
        lst.Add obj, CStr(i)
    Next
    
    sw_.split "Object 代入:"
    
    For i = 0 To lst.Count - 1
        Set obj = lst(CStr(i))
    Next
    
    sw_.split "呼出:"

End Sub

実行結果の1例がこちら

**コレクション-キー付き**
Long 代入:0.890625s
呼出:0.3671875s
--:0s
Object 代入:0.984375s
呼出:0.359375s
**終了** :2.65625s

通常のコレクションに比べ、若干代入は遅いのですが、
呼び出しは桁違いに速いです。(恐らくForだからだと思います)

ダメだ、眠い。 続きは後日に。

リファクタリング

昨年末の予想通りすっかり余裕が無く、更新していませんでした。

あまり書ける事が無いものの、Bingチャット(Copilot)は
チョロチョロ試しています。
ある処理でもうちょっと良い方法が無いものかと思い、
試した所、ソコソコの長さのものが一つの関数で出力されました。

"いくら何でも、おかしい・・・"と思い、追加の注文(背景紫が僕)

逆切れ・・・とまでは言いませんが、まさか逆に"教えろ"と言われるとは
思っていませんでした。
(具体的に何処をリファクタリングすべきか指摘したら、確かに行いましたが
指摘箇所以外はしてくれませんでした)

リファクタリングのメリット・デメリットは、先人の方々がいっぱい記載
してくれているので割愛しますが、個人的な意味合いとタイミングです。

一つの関数に大量の処理を行うと、デバッグ時のブレークポイント
止めた際、変数がドバっと大量に表示されチェックすべき項目が増えて為
憂鬱になりませんかね?
そもそも変数が多くなると、変数名に悩みませんか?僕の場合、
ボキャブラリー不足で名前を決めるだけで悩んじゃいます。

逆に小さな処理を行うシンプルな関数であれば、変数が数個だけの
場合が多いのでシンプルな変数名でも構わないと思ってます。
その様な場合でも、関数名や引数名は若干長くても分かりやすい
名前にすべきだと思ってます。

例えば

function get_points() as variant

より

function get_points_by_sketch() as variant

の方が、明確でしょう。

コレクション内のオブジェクトのプロパティを利用したソート2

こちらの続きです。
C#ATIA

Bingチャットも良いのですが、こちらのコメント欄に記載した
phindと言うサービスの存在を知りました。
Bingチャットでコードレビュー - C#ATIA

コーディングに関してはGPT-4を上回ると言う事らしいですが、
それ以上にBingチャットとは比較にならない程、返答が速いです。

同様の質問をした際の返答の回答部分はこの様な感じでした。

Function DistanceFromOrigin() As Double
   DistanceFromOrigin = Sqr(Me.x ^ 2 + Me.y ^ 2)
End Function


Function ComparePoints(point1 As Point2D, point2 As Point2D) As Long
   If point1.DistanceFromOrigin < point2.DistanceFromOrigin Then
       ComparePoints = -1
   ElseIf point1.DistanceFromOrigin > point2.DistanceFromOrigin Then
       ComparePoints = 1
   Else
       ComparePoints = 0
   End If
End Function


Sub SortPoints(points As Collection)
   Dim i As Long
   Dim j As Long
   Dim temp As Variant
   Dim hasSwapped As Boolean

   Do
       hasSwapped = False
       For i = 1 To points.Count - 1
           For j = i + 1 To points.Count
               If ComparePoints(points(i), points(j)) > 0 Then
                  Set temp = points(i)
                  Set points(i) = points(j)
                  Set points(j) = temp
                  hasSwapped = True
               End If
           Next j
       Next i
   Loop Until Not hasSwapped
End Sub

肝心のソート部分が"Function"では無く"Sub"だったので、
望んでいるものでは無かったのですが、Bingチャット同様に
スワップ処理をコレクション要素の再代入になっている為、
NGです。

その後何度かエラーを指摘し得られた回答のコード部分が
こちらです。

Public Function ComparePoints(point1 As Point2D, point2 As Point2D) As Long
 Dim distance1 As Double
 Dim distance2 As Double

 distance1 = Sqr((point1.x ^ 2) + (point1.y ^ 2))
 distance2 = Sqr((point2.x ^ 2) + (point2.y ^ 2))

 If distance1 < distance2 Then
     ComparePoints = -1
 ElseIf distance1 > distance2 Then
     ComparePoints = 1
 Else
     ComparePoints = 0
 End If
End Function


Public Function SortPoints(points As Collection) As Collection
  Dim arr() As Point2D
  Dim sortedPoints As New Collection
  Dim i As Long
  Dim j As Long
  Dim temp As Point2D

  ' Step 1: Copy all elements to temporary array
  ReDim arr(1 To points.Count)
  For i = 1 To points.Count
      Set arr(i) = points(i)
  Next i

  ' Step 2: Sort the array using bubble sort
  For i = 1 To points.Count - 1
      For j = i + 1 To points.Count
          If ComparePoints(arr(i), arr(j)) > 0 Then
              ' Swap arr(i) and arr(j)
              Set temp = arr(i)
              Set arr(i) = arr(j)
              Set arr(j) = temp
          End If
      Next j
  Next i

  ' Step 3: Create new sorted collection
  For i = LBound(arr) To UBound(arr)
      sortedPoints.Add arr(i)
  Next i

  Set SortPoints = sortedPoints
End Function

Bingチャット同様で、

コレクション→配列→ソート→コレクション

で処理されています。もうVBAでは定石な手段なのでしょう。


実は、年末に試した際に一回目の回答で得られたものに驚きました。
(控えていない上、試しに実行もしませんでした。スイマセン)
ソート関数の引数で、ソート条件をaddressOfで渡しているサンプルを
書き出したのですが、良く調べたらVBAではアドレスから関数の
呼び出しは基本的には出来ない仕様のようなので、恐らく
動かなかったかな・・・。

addressOfを利用する方法を解説しているサイトは、ここぐらいでしょう。
いげ太の日記


phindはVSCodeの拡張があります。VBAVSCodeで開発出来ないかな・・・。

コレクション内のオブジェクトのプロパティを利用したソート1

あけましておめでとうございます。
本年もマイペースでやっていきます。

年明け早々、あの様な震災があったり、それに伴った人災(航空機の接触)と
心痛む出来事の連続で唖然としつつ、普通に正月を迎えられることが
非常に幸せな事なのだと感じております・・・。
(ご冥福をお祈りいたします)

あぁ今年は目標を立てない事にします。昨年一つも達成しなかったので。


昨年の11月に、こちらをチラッと書きました。
ボディの体積をダンプする5 - C#ATIA
ここではCATIAのオブジェクトを利用したソートになっていますが、
興味があるのはそういう事ではなく、
"コレクション内のオブジェクトのプロパティを利用したソート"
についてがテーマでした。
これは僕にとってVBAの大きな疑問です。

昨年末頃にこちらを試しました。
Bingチャットでコードレビュー - C#ATIA
その際、気づかなかったのですが、後で"AIはどんな処理を書くの?"と
言う疑問が湧き、試した所これが中々面白かったです。

実は年末にBingチャットで試した際、最初はJavaのコードを答えられ、
"VBAで教えて下さい”と追加質問したところ、
"他の言語は学習していますがVBAは学習していない為、答えられません"
のような回答となりました。
(思わず、BingはVBAを答えないとダメだろ と突っ込みました)


今日、(恐らく質問文が異なっていると思います)再度質問したところ
答えてくれました。
念の為、こちらの文章で質問してみました。

VBAで次のPoint2Dクラスモジュールがあります。
'vba Point2D.cls

Option Explicit

public x As Double
public y As Double

このPoint2Dクラスのインスタンスを複数保持しているPointsコレクションがあります。

このコレクションを原点から近い順にソートされたコレクションが得られるVBAの関数を教えて下さい。

Bingチェットが答えてくれたコードのみです。

'Point2Dクラスのインスタンスの距離を計算する関数
Function Distance(p As Point2D) As Double
    Distance = Sqr(p.x ^ 2 + p.y ^ 2)
End Function


'Pointsコレクションを原点から近い順にソートする関数
Function SortByDistance(points As Collection) As Collection
    Dim i As Long, j As Long
    Dim n As Long
    Dim temp As Point2D
    n = points.Count 'コレクションの要素数
    'バブルソートのアルゴリズム
    For i = 1 To n - 1
        For j = i + 1 To n
            '距離が小さいものを前に移動する
            If Distance(points(j)) < Distance(points(i)) Then
                Set temp = points(i)
                Set points(i) = points(j)
                Set points(j) = temp
            End If
        Next j
    Next i
    'ソートされたコレクションを返す
    Set SortByDistance = points
End Function

これだけでは試す事が出来ない為、幾つかの関数を用意しました。
(これをBingチャットに書かせる自信が無いので・・・)

'エントリーポイント
Sub main()
    Dim coordinates As Variant
    coordinates = Array( _
        Array(0, 1), _
        Array(2, 12), _
        Array(5, 5), _
        Array(20, 3), _
        Array(8, 2), _
        Array(10, 5), _
        Array(0, 2) _
    )
    
    Dim points As Collection
    Set points = New Collection
    
    Dim pnt As Point2D
    Dim i As Long
    For i = 0 To UBound(coordinates)
        Set pnt = New Point2D
        pnt.x = coordinates(i)(0)
        pnt.y = coordinates(i)(1)
        points.Add pnt
    Next
    
    dump_points points, "** before **"

    Dim sortedPoints As Collection
    Set sortedPoints = SortByDistance(points)

    dump_points sortedPoints, "** after **"
End Sub


Private Sub dump_points( _
    ByVal points As Collection, _
    Optional ByVal msg As String = "")
    
    If Len(msg) > 0 Then
        Debug.Print msg
    End If
    
    Dim pnt As Point2D
    For Each pnt In points
        dump_point pnt
    Next
End Sub


Private Sub dump_point( _
    ByVal point As Point2D)
    
    Debug.Print "X:" & point.x & " Y:" & point.y
End Sub

又、質問時に定義している"Point2D.cls"クラスモジュールも念の為
記載しておきます。

'vba Point2D.cls

Option Explicit

Public x As Double
Public y As Double

用意が整ったので、実行したところエラーとなりました。
この部分でのエラーです。

                Set points(i) = points(j)

・・・実行する前から気が付いていましたが、念の為実行してみました。

これはコレクション内の要素を書き換えようとした際の部分ですが、
そこでエラーとなります。

そうなんです、VBAのコレクションは内部の要素の上書きが出来ないんです。
ソート処理には幾つかアルゴリズムがありますが(僕はあまり詳しくないです)
何れもスワップ処理が必要となりますが、VBAのコレクションは要素の
上書きが出来ない為、この部分が非常にネックとなるはずです。
(コレクションのRemoveメソッドと挿入位置指定のAddメソッドで
出来ない事は無いはずですが、面倒です)

続いてBingチャットにこちらを質問しました。

VBAのコレクションは要素の再代入が出来ない為エラーになります。

得られた回答のコード部分です。

'Point2Dクラスのインスタンスの距離を計算する関数
Function Distance(p As Point2D) As Double
    Distance = Sqr(p.x ^ 2 + p.y ^ 2)
End Function


'Pointsコレクションを原点から近い順にソートする関数
Function SortByDistance(points As Collection) As Collection
    Dim i As Long, j As Long
    Dim n As Long
    Dim temp As Point2D
    Dim arr() As Point2D '配列を宣言
    n = points.Count 'コレクションの要素数
    ReDim arr(1 To n) As Point2D '配列のサイズを指定
    'コレクションの要素を配列にコピー
    For i = 1 To n
        Set arr(i) = points(i)
    Next i
    'バブルソートのアルゴリズム
    For i = 1 To n - 1
        For j = i + 1 To n
            '距離が小さいものを前に移動する
            If Distance(arr(j)) < Distance(arr(i)) Then
                Set temp = arr(i)
                Set arr(i) = arr(j)
                Set arr(j) = temp
            End If
        Next j
    Next i
    'コレクションを空にする
    For i = n To 1 Step -1
        points.Remove i
    Next i
    '配列の要素をコレクションに追加
    For i = 1 To n
        points.Add arr(i)
    Next i
    'ソートされたコレクションを返す
    Set SortByDistance = points
End Function

結論からすると正しく処理出来るものになっていました。

結局、

コレクション→配列→ソート→コレクション

の処理を行っています。正直な所、予想通りです。

VBAの悩ましい部分がここなんです。
・コレクション:代入する際の要素数は自動拡張するが、再代入が出来ない。
・配列:再代入可能だが、要素数は自動拡張しない。
細かな事を言えば、メモリの確保の仕方の違いなどもあるとは思いますが、
何時も"どっち使う?"になってしまいます。(個人的には配列)

もうちょっと試しているのですが、長くなったので次回に。

2023年を振り返る

ちょっと早い様な気もしますが、今年はもう店じまいです。

年頭に、こちらで"答え合わせするつもりもない"と記載しました。
2023年の目標 - C#ATIA
・・・何一つ達成していません。Autodesk App Store
アップ出来なかった事は非常に悔やまれます。


〇CATIA
今年は近年まれなぐらいに書いた気がします。が、これと言って
有用なものも作れなかったような・・・。
終盤にpythonで挑戦出来たことは良い経験になりましたが、
継続するかどうかは不明です。


Fusion360
年頭の目標が全く達成しなかった原因は、4月のアップデートで
CAMのAPIが強化され、そちらに取り組む時間が大幅に増えた事に
よるものです。
とは言え、こちらも成果のあるものが作成出来ず何とも言い難い・・・。
又、その節は期待だけさせて停止させてしまった案件があり
大変申し訳なく思っております。


〇その他
偶然ですが"Flet"と言うpython用のフレームワークを見つけ、少し
取り組みました。普段CADやCAMが関連するものしか興味が無いの
ですが、逆に関連しない様なGUIアプリを作成するには良い
フレームワークだと感じました。
(幸いこちら関連のアクセスが少しありました)
但し、DOMに関するお勉強の必要性は感じましたね。


最後となりますが、諸事情により20年強勤めていた企業を退社する
ことに致しました。それまではCATIAはもちろん、CADやCAMソフト
自体を扱う事すらなかった自分に、それらを扱い商売にする経験を
させてもらえた事に感謝しています。
来年からは新たな企業に勤める事となり、どの様な生活となるのか
分かっておりませんが、最初は不慣れな生活となる事は間違いないと
思っております。
本業にいち早く慣れ、早い段階で結果を出す必要性があるとも
感じており、当面はこちらのブログでの更新ペースは今まで程では
なくなる可能性も感じています。
但し9年程続けてきて、学習はインプットはもちろんアウトプットの
必要性を自分自身で体感しているので、何かしら継続出来れば
とは思っております。
(アウトプットをやったことの無い方は、是非やってみて下さい)


では、皆様良いお年を!!