猫も杓子もChatGPTな昨今。
遅ればせながらChatGPTデビューしてみました。
ChatGPTとは
AIを利用したまるで人間同士で会話をしているような文章を作成することが出来るツールです。
過去の膨大なデータを参照しあらゆる事柄に専門家のように回答を返してくれます。
過信するべからず
ただ現時点(2023/2/7)では回答は完璧なものではありません。
質問がはっきりした内容でないにも関わらず、意図を読み取ってくれているのは凄いです。
実際の知りたい内容、「魏」「呉」「蜀」の名称については完璧に回答しています。
が、その説明に誤りがあります。
回答を返してくれたからと言って、全てを鵜呑みにする訳にはいかないようです。
リファクタリング
では、本日の本題ではChatGPTにリファクタリングを行ってもらいます。
ウィキペディアによるとリファクタリングとは「プログラムの外部から見た動作を変えずにソースコードの内部構造を整理することである」となっています。
今回は私が過去に作成したアドインをリファクタリングしてもらいます。
元コード
今回のリファクタリングしてもらうアドインはこちら。
www.excellovers.com
オートフィルタがかかっている列を抽出するアドインです。
横長のシートにオートフィルタがかかっている時に、どの列でフィルタがかかってるんだよ!ていうのを解消するものです。
以下が元々作成していたコードです。
このコードをCHatGPTにリファクタリングしてもらいました。
Function FilterSearch(AllColumnSearch As Boolean, FilterType As String) As Variant Dim i As Long Dim n As Long Dim myVar As Variant Dim myFilterObj As Object Dim ColumnsCount As Long Set myFilterObj = Nothing If FilterType = "オートフィルター" Then Set myFilterObj = ActiveSheet.AutoFilter ColumnsCount = myFilterObj.Filters.Count Else Dim myListObj As ListObject For Each myListObj In ActiveSheet.ListObjects If myListObj.Name = FilterType Then Set myFilterObj = ActiveSheet.ListObjects(FilterType).AutoFilter ColumnsCount = ActiveSheet.ListObjects(FilterType).ListColumns.Count End If Next End If If Not myFilterObj Is Nothing Then With myFilterObj ReDim myVar(ColumnsCount) Dim ListValuesArray(1 To 3) As Variant For i = 1 To ColumnsCount If AllColumnSearch Or .Filters(i).On Then ListValuesArray(1) = .Range.Cells(i).Value ListValuesArray(2) = .Range.Cells(i).Address(False, False) ListValuesArray(3) = "" If .Filters(i).On Then On Error Resume Next Dim myFilterCriteria1 As Variant myFilterCriteria1 = "" myFilterCriteria1 = myFilterObj.Filters(i).Criteria1 On Error GoTo 0 If myFilterObj.Filters(i).Operator = xlFilterCellColor Then ListValuesArray(3) = "色フィルタ" ElseIf IsEmpty(myFilterCriteria1) Then '日付け列の場合、エラーが返ってくるため。 ListValuesArray(3) = "" ElseIf IsArray(myFilterCriteria1) Then ListValuesArray(3) = Join(myFilterCriteria1) Else ListValuesArray(3) = myFilterCriteria1 End If End If myVar(n) = ListValuesArray n = n + 1 End If Next End With Else n = 0 End If If n = 0 Then myVar = Array() Else ReDim Preserve myVar(n - 1) End If FilterSearch = myVar End Function
リファクタリング後
ChatGPTにリファクタリングしてもらうのは簡単です。
「リファクタリングして」と記入してから、コードを貼り付けるだけです。
特にどのプログラミング言語を使用していると記載しなくても理解できるようです。
以下がChatGPTの返答です。
The function FilterSearch
returns an array of arrays, each containing information about a column in a sheet with a filter applied. The parameters AllColumnSearch
and FilterType
determine which sheet and which columns to retrieve information from.
The code could be improved by simplifying the logic and reducing the number of intermediate variables. Here's a refactored version:
Function FilterSearch(AllColumnSearch As Boolean, FilterType As String) As Variant Dim myObj As Object Dim ColumnsCount As Long Dim i As Long Dim n As Long Dim myVar() As Variant Set myObj = GetFilterObject(FilterType) If myObj Is Nothing Then FilterSearch = Array() Exit Function End If ColumnsCount = GetColumnsCount(myObj, FilterType) ReDim myVar(0 To ColumnsCount - 1) n = 0 With myObj For i = 1 To ColumnsCount If AllColumnSearch Or .Filters(i).On Then ReDim Preserve myVar(0 To n) myVar(n) = GetColumnInfo(.Filters(i), .Range.Cells(i)) n = n + 1 End If Next End With FilterSearch = myVar End Function Private Function GetFilterObject(FilterType As String) As Object Dim myListObj As ListObject Set GetFilterObject = Nothing If FilterType = "オートフィルター" Then Set GetFilterObject = ActiveSheet.AutoFilter Else For Each myListObj In ActiveSheet.ListObjects If myListObj.Name = FilterType Then Set GetFilterObject = myListObj.AutoFilter Exit For End If Next End If End Function Private Function GetColumnsCount(myObj As Object, FilterType As String) As Long If FilterType = "オートフィルター" Then GetColumnsCount = myObj.Filters.Count Else GetColumnsCount = ActiveSheet.ListObjects(FilterType).ListColumns.Count End If End Function Private Function GetColumnInfo(filter As Object, cell As Object) As Variant Dim ListValuesArray(1 To 3) As Variant ListValuesArray(1) = cell.Value ListValuesArray(2) = cell.Address(False, False) ListValuesArray(3) = "" If filter.On Then On Error Resume Next Dim myFilterCriteria1 As Variant myFilterCriteria1 = filter.Criteria1 On Error GoTo 0 If filter.Operator = xlFilterCellColor Then ListValuesArray(3) = "色フィルタ" ElseIf IsEmpty(myFilterCriteria1) Then 'do nothing ElseIf IsArray(myFilterCriteria1) Then ListValuesArray(3) = Join(myFilterCriteria1) Else ListValuesArray(3) = myFilterCriteria1 End If End If GetColumnInfo = ListValuesArray End Function
日本語で聞いたら日本語で返してくれるんですが、質問内容がコードで英語ばかりだったためか返答が英語でした。
Google翻訳で訳してみると
関数
FilterSearch
は配列の配列を返します。各配列には、フィルターが適用されたシート内の列に関する情報が含まれています。パラメータ「AllColumnSearch」と「FilterType」は、どのシートとどの列から情報を取得するかを決定します。 ロジックを単純化し、中間変数の数を減らすことで、コードを改善できます。リファクタリングされたバージョンは次のとおりです。
凄いですね。 コードの内容を理解してコメントしてくれています。 改善点も判断した上でリファクタリングしてくれた様子が伺えます。
リファクタリングされたコードはかなり変更されています。
ですがもちろん変更後のコードもそのまま貼り付けるだけで使用できる状態です。
元々1つのプロシージャで記載していたものを機能別に切り分けで4つに分割されています。 そうすることでメインコードである「FilterSearch」はかなり簡素化されて読みやすくなっています。
はい!採用!
参考までに見てみようと思っただけだったけどこのコード採用します!
でもですね、このコード、機能的にちょっと見落としがあるんですね。
元のコードの一番最後にあるIF文の
'元コード抜粋 myVar = Array()
この一文の処理が抜けていました。 これはフィルター絞り込みがされていない時に空配列を返すようにしています。 これが抜けているため、オートフィルタで絞り込みがかかっていない時にエラーになります。
リファクタリング後の下記のコードでは考慮されていたのに惜しいですね。
'FilterSearch抜粋 If myObj Is Nothing Then FilterSearch = Array() Exit Function End If
やはり、どんな質問の回答にしろ参考にしても疑ってかかる必要がありますね。
リファクタリング後修正
こちらがChatGPT案を参考ほぼそのまま に修正したものです。
Function FilterSearch(AllColumnSearch As Boolean, FilterType As String) As Variant Dim myFilterObj As Object Set myFilterObj = GetFilterObject(FilterType) If myFilterObj Is Nothing Then FilterSearch = Array() Exit Function End If Dim ColumnsCount As Long ColumnsCount = GetColumnsCount(myFilterObj, FilterType) Dim myVar As Variant: ReDim myVar(0 To 0) Dim n As Long: n = 0 Dim i As Long With myFilterObj For i = 1 To ColumnsCount If AllColumnSearch Or .Filters(i).On Then ReDim Preserve myVar(0 To n) myVar(n) = GetColumnInfo(.Filters(i), .Range.Cells(i)) n = n + 1 End If Next End With If n = 0 Then myVar = Array() FilterSearch = myVar End Function '---------------------------------------------------------------------- Private Function GetFilterObject(FilterType As String) As Object Dim myListObj As ListObject Set GetFilterObject = Nothing If FilterType = "オートフィルター" Then Set GetFilterObject = ActiveSheet.AutoFilter Else '呼び出し元ListboxAddで選別されるので存在しないテーブル名が渡されることはない For Each myListObj In ActiveSheet.ListObjects If myListObj.Name = FilterType Then Set GetFilterObject = myListObj.AutoFilter Exit For End If Next End If End Function '---------------------------------------------------------------------- Private Function GetColumnsCount(myObj As Object, FilterType As String) As Long If FilterType = "オートフィルター" Then GetColumnsCount = myObj.Filters.Count Else GetColumnsCount = ActiveSheet.ListObjects(FilterType).ListColumns.Count End If End Function '---------------------------------------------------------------------- Private Function GetColumnInfo(myFilter As Object, myRng As Object) As Variant Dim ListValuesArray(1 To 3) As Variant ListValuesArray(1) = myRng.Value ListValuesArray(2) = myRng.Address(False, False) ListValuesArray(3) = "" If myFilter.On Then On Error Resume Next Dim myFilterCriteria1 As Variant myFilterCriteria1 = "" '初期化。下記でエラーが有った場合に前の値が残ってしまう myFilterCriteria1 = myFilter.Criteria1 On Error GoTo 0 If myFilter.Operator = xlFilterCellColor Then ListValuesArray(3) = "色フィルタ" ElseIf IsEmpty(myFilterCriteria1) Then 'do nothing。日付け列の場合、エラーが返ってくるため。どうすれば取得できる? ElseIf IsArray(myFilterCriteria1) Then ListValuesArray(3) = Join(myFilterCriteria1) Else ListValuesArray(3) = myFilterCriteria1 End If End If GetColumnInfo = ListValuesArray End Function
問題のオートフィルタがかかっていないときの対応は、メインコードのFilterSearchの一番最後に下記を追加しました。
If n = 0 Then myVar = Array()
これでオートフィルタがかかっていない時に空配列を返すようになりますので、後続の処理でエラーが出なくなりました。
まとめ
これからの時代、コードはゼロから書くのではなくAIにやりたいことを質問して提示されたコードを流用していく、あるいは今回のようにリファクタリングをしてもらいコードをより良くしていくような書き方が当たり前になっていくのかもしれません。
専業のプラグラマーではないからこそ、こういう機能を上手く取り入れていけるようになっておきたいです。