cやc++の関数ポインタは、相手に処理を渡したいというときに非常に便利なので、別言語でもそれっぽいことがしたいです。
以前の記事ではJavaでFunctionを使ってこれを実現しました。
今回は.NetのFunc
やり方
受け取って使う
以下は簡単なクイックソートのコードです。Comp As Func(Of Tuple(Of T, T), Boolean)の部分が関数を受け取る部分です。
c++のsort関数では比較処理をラムダで渡したりしますが、あれと同じです。
Function QSort(Of T)(ByVal Array As T(), ByRef Comp As Func(Of Tuple(Of T, T), Boolean)) As T() If (Array.Length < 3) Then If (Array.Length = 2 AndAlso Not Comp(New Tuple(Of T, T)(Array(0), Array(1)))) Then SWAP(Array(0), Array(1)) Return Array End If Dim big As New List(Of T) Dim small As New List(Of T) For i = 1 To Array.Length - 1 If Comp(New Tuple(Of T, T)(Array(0), Array(i))) Then big.Add(Array(i)) Else small.Add(Array(i)) End If Next Dim result As New List(Of T)(QSort(small.ToArray, Comp)) From { Array(0) } result.AddRange(QSort(big.ToArray, Comp)) Return result.ToArray End Function
渡す
関数を渡す方法としては、ラムダを渡すか、関数を定義してAdressOfで渡すかの2通りあります。オマケとして、関数を用意しておけば反転はラムダで簡単に書けますね。
Sub Main() Dim array = {"dd", "aaa", "eee", "cccc", "b"} '長さソート Console.WriteLine("Length") For Each str As String In QSort(array, Function(ByVal t As Tuple(Of String, String)) As Boolean Return (t.Item1.Length < t.Item2.Length) End Function) Console.WriteLine(str) Next '1文字目ソート Console.WriteLine("First Char") For Each str As String In QSort(array, AddressOf CompFirst_LT) Console.WriteLine(str) Next '1文字目逆順 Console.WriteLine("First Char(reverse)") For Each str As String In QSort(array, Function(ByVal t As Tuple(Of String, String)) As Boolean Return Not CompFirst_LT(t) End Function) Console.WriteLine(str) Next Console.ReadKey() End Sub
雑感
それでも関数ポインタのが便利だと思います……。
オマケ
コード全文
Module Module1
Sub SWAP(Of T)(ByRef p1 As T, ByRef p2 As T)
Dim temp = p1
p1 = p2
p2 = temp
End Sub
Function QSort(Of T)(ByVal Array As T(), ByRef Comp As Func(Of Tuple(Of T, T), Boolean)) As T()
If (Array.Length < 3) Then
If (Array.Length = 2 AndAlso Not Comp(New Tuple(Of T, T)(Array(0), Array(1)))) Then SWAP(Array(0), Array(1))
Return Array
End If
Dim big As New List(Of T)
Dim small As New List(Of T)
For i = 1 To Array.Length - 1
If Comp(New Tuple(Of T, T)(Array(0), Array(i))) Then
big.Add(Array(i))
Else
small.Add(Array(i))
End If
Next
Dim result As New List(Of T)(QSort(small.ToArray, Comp)) From {
Array(0)
}
result.AddRange(QSort(big.ToArray, Comp))
Return result.ToArray
End Function
Function CompFirst_LT(ByVal t As Tuple(Of String, String)) As Boolean
Return t.Item1(0) < t.Item2(0)
End Function
Sub Main()
Dim array = {"dd", "aaa", "eee", "cccc", "b"}
'長さソート
Console.WriteLine("Length")
For Each str As String In QSort(array,
Function(ByVal t As Tuple(Of String, String)) As Boolean
Return (t.Item1.Length < t.Item2.Length)
End Function)
Console.WriteLine(str)
Next
'1文字目ソート
Console.WriteLine("First Char")
For Each str As String In QSort(array,
AddressOf CompFirst_LT)
Console.WriteLine(str)
Next
'1文字目逆順
Console.WriteLine("First Char(reverse)")
For Each str As String In QSort(array,
Function(ByVal t As Tuple(Of String, String)) As Boolean
Return Not CompFirst_LT(t)
End Function)
Console.WriteLine(str)
Next
Console.ReadKey()
End Sub
End Module
実行結果
Length b dd eee aaa cccc First Char aaa b cccc dd eee First Char(reverse) eee dd cccc b aaa