LoginSignup
2
3

More than 5 years have passed since last update.

配列のクリップボード転送(VBAHaskell)

Last updated at Posted at 2016-12-11

これは、Visual Basic Advent Calendar 2016の12日目の記事です。

1. テーマ

2次元以下の配列のデータをクリップボードに転送します。
行内はTabで、行間はCrLfで繋げた文字列形式で出力します。文字列にできない要素(NullやEmpty、配列など)は長さゼロの文字列""とします。

a = makeM(3, 4, iota(1,12))                  ' 配列の作成
a(0,1) = Null : a(1,2)= "az" : a(2,1)= "愛"  ' Nullや文字を入れる
m2Clip  a                                    ' クリップボード転送

m2Clip」でクリップボードに転送し、エディタ等にペーストできます。
image

転送対象の文字列を作る部分が当記事のメインです。

2. コード

最初にコードを示します。モジュールは、Y_IO_utiliy.bas(Github)またはY_IO_utiliy.basに置いています。

' 配列(2次元以下)をクリップボードに転送する
Public Sub m2Clip(ByRef data As Variant)
    Dim s As String
    ' -------------------------
    ' ↓ 前半部分。クリップボードに貼り付けるための文字列を作る
    Select Case Dimension(data)
    Case 0
        s = CStr_(data) & vbCrLf
    Case 1
        s = Join(mapF(p_CStr, data), vbTab) & vbCrLf
    Case 2
        Dim tmp As Variant
        tmp = zipC(mapF(p_CStr, data))
        tmp = mapF(p_join(, vbTab), tmp)
        s = Join(tmp, vbCrLf) & vbCrLf
    End Select
    ' ↑ 前半部分ここまで。
    ' -------------------------
    ' ↓ 後半部分。文字列をクリップボードに貼り付ける
    Dim dOb As Object
    'Set dOb = New MSFORMS.DataObject
    Set dOb = CreateObject("new:{1C3B4210-F441-11CE-B9EA-00AA006B1A69}")
    With dOb
        .SetText s
        .PutInClipboard
    End With
    Set dOb = Nothing
End Sub

前半部分で、配列の次元 (0, 1, 2) によって場合分けしています。
Case 0は実際には配列ではなくてスカラーです。関数CStr_はVBA組み込みのCStrをちょっと変えたもので、文字列にできるデータは文字列化し、できないデータは長さゼロの文字列""にするだけの関数です。

Case 1は1次元配列、Case 2は2次元配列で、ここに出てくるmapFはVBAHaskellの関数で最も頻繁に使われるものです。

3. 配列要素のString変換

dataが1次元配列の場合、本質的には Join(data, vbTab) & vbCrLf とするだけですが、要素の型によってはJoinがエラーになる1ので事前にStringに変換する必要があります。
 mapF(p_CStr, data)   'p_CStr は CStr_ の関数オブジェクト
このマッピングによって、data = { x0, x1, x2, ... , xn } として
{x0, x1, x2, ... , xn}  :point_right_tone4:  {CStr_(x0) , CStr_(x1) , CStr_(x2) , ... , CStr_(xn)}
と変換されます。
7日目の記事 でも書きましたが、関数本体であるCStr_は普通のVBA関数で、p_CStrはそのアドレス(AddressOfの値)を含む一種の関数オブジェクト(p_関数)です(実体は配列)。

dataが2次元配列x(i, j)のときの手順は以下の通りです。
 (1) 各要素を文字列化する
   tmp = mapF(p_CStr, data)
 (2) 行単位で配列化する
   tmp = zipC(tmp)
 (3) 各行に対してJoin( , vbTab)する
   tmp = mapF(p_join(, vbTab), tmp)
 (4) Join( , vbCrLf)し、vbCrLfを追加する
   s = Join(tmp, vbCrLf) & vbCrLf

(1) は前に説明した通りです。
(2) Joinは配列自体を引数に取るので、行を一つの単位として配列化する必要があります。zipCはジャグ配列を作る関数の一つで、下にその概念を図で示しています。
(3) も前に説明した通りですが、mapFを使ってループを書かずに済んでいます。
(4) 行間はTabではなくCrLfで繋いでいます。

'ふたつの配列の対応する要素どうしをmakePairしてジャグ配列を作る
Public Function zip(ByRef a As Variant, ByRef b As Variant) As Variant

'2次元配列の各行ベクトルをzipVs
Public Function zipR(ByRef m As Variant, Optional ByRef target As Variant) As Variant

'2次元配列の各列ベクトルをzipVs
Public Function zipC(ByRef m As Variant, Optional ByRef target As Variant) As Variant

image

実際のコードでは(1)と(2)を合わせてtmp = zipC(mapF(p_CStr, data))と書いています。

4. クリップボード転送

Officeアプリによっては参照設定にMSFORMS.DataObjectが選べないようなので、New MSFORMS.DataObjectではなくCreateObjectを使っています。

DataObject::SetTextメソッドはUnicodeで書き込まれるせいか、カナ漢字等が含まれている場合はVBAのモジュールやイミディエイト ウィンドウにはペーストできません。最初クリップボード転送自体が失敗しているのかと思いましたが、外部エディタやワークシートにはペーストできるのでよしとしています2。 イミディエイト ウィンドウに配列を出力するのはprintM関数が使えます。


リンク

VBAHaskellのI/Oユーティリティ
Qiita VBAHaskellの紹介 その1 (最初はmapF)
ソースコード(Github)
VBAHaskellの関数リファレンス
dllバイナリ:
https://github.com/mYmd/VBA/blob/master/bin/mapM (32bit-Office用)
https://github.com/mYmd/VBA/blob/master/bin/mapM64 (64bit-Officey用)
VBAコード添付済みExcelブック
VBAHaskellほぼ全部入り.xlsm


  1. Nullや配列などが含まれているとエラーになります 

  2. Win32APIを使った方法もあるみたいですが、やる気がないです 

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3