ChatGPTに3回質問して「文字の類似度」のサジェストするプログラム作成

# 概要

今回は、ChatGPTに**レーベンシュタイン距離**を使った文字の類似度を測定しサジェストを作成するプログラムを、3回の質問でプログラムを書いて貰います。

**ChatGPT**への質問内容をテクニックを交えてを紹介します。


# レーベンシュタイン距離とは


レーベンシュタイン距離は、二つの文字列がどの程度異なっているかを示す距離である。
（[Wikipediaより引用](https://ja.wikipedia.org/wiki/%E3%83%AC%E3%83%BC%E3%83%99%E3%83%B3%E3%82%B7%E3%83%A5%E3%82%BF%E3%82%A4%E3%83%B3%E8%B7%9D%E9%9B%A2)）


要するに、**計測した数値が低いものほど、文字の類似度が高い**ということです。


# 質問１：丸投げする

とりあえず、ChatGPTに丸投げします。

「買い物するアプリを作って」など抽象的なものではなく、具体的な機能の関数やメソッドなど**具体的**な内容で質問することを意識しています。

## 質問内容

```:質問内容
「レーベンシュタイン距離」を使った文字列の類似度を測定するプログラムをjavascriptで書いて！
```

## ChatGPTの回答

結果が返ってきました。for文を多用していますが、比較的シンプルなコードになっているのかなと思います。

```js:chatGPTの回答
function levenshteinDistance(str1, str2) {
  const len1 = str1.length;
  const len2 = str2.length;
  
  const dp = [];
  
  for (let i = 0; i <= len1; i++) {
    dp[i] = [];
    for (let j = 0; j <= len2; j++) {
      if (i === 0) {
        dp[i][j] = j;
      } else if (j === 0) {
        dp[i][j] = i;
      } else {
        dp[i][j] = 0;
      }
    }
  }
  
  for (let i = 1; i <= len1; i++) {
    for (let j = 1; j <= len2; j++) {
      if (str1[i - 1] === str2[j - 1]) {
        dp[i][j] = dp[i - 1][j - 1];
      } else {
        dp[i][j] = 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);
      }
    }
  }
  
  return dp[len1][len2];
}

// 使用例
const string1 = "猫";
const string2 = "犬";
const distance = levenshteinDistance(string1, string2);
console.log(`"${string1}" と "${string2}" のレーベンシュタイン距離は ${distance} です。`);
```

## 実行結果

実際に動かしてみました。うまく動いていそうです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/473097/c1d3f98f-5414-fdc8-a933-69aaf173f7c7.png)



# 質問２：自分で書いたコードをリファクタリングしてもらう

先ほど作ってもらった`levenshteinDistance()`を利用して、複数のキーワードと類似度を比較して、類似度が高いものをいくつか返してもらう`getSuggest()`を作りました。

見ての通り、綺麗なコードではありません。
（変化が分かりやすいようにあえて汚く書きました！）
このコードをリファクタリングしてもらいます。

```js:自分で書いたコード
function getSuggest(searchText, wordList, size) {
    const data = new Object();
    wordList.forEach((s) => {
        data[s] = levenshteinDistance(searchText,s);
    });
    const sortedData = Object.entries(data).sort((a, b) => a[1] - b[1]);
    r = [];
    sortedData.forEach((d)=>{
        k = d[0];
        v = d[1];
        r[k] = v;
    });
    return Object.keys(r).slice(0, size);
}

const searchText = "太田";
const wordList = ['田中', '中山', '山田', '中田', '山中', '田山'];
const size = 3;
console.log(getSuggest(searchText,wordList,size));
```

## 質問内容

```:質問内容
下記コードを簡潔に書いてほしいなー
function getSuggest(searchText, wordList, size) {
    const data = new Object();
    wordList.forEach((s) => {
        data[s] = levenshteinDistance(searchText,s);
    });
    const sortedData = Object.entries(data).sort((a, b) => a[1] - b[1]);
    r = [];
    sortedData.forEach((d)=>{
        k = d[0];
        v = d[1];
        r[k] = v;
    });
    return Object.keys(r).slice(0, size);
}
```

## ChatGPTの回答

前回の質問あって、質問内容は、関数部分だけで読み取ってくれました。さすがです。
とても可読性の高い、シンプルなコードになりました。

（本来は、何度か繰り返したり、ChatGPTの回答結果をさらにリファクタリングしてもらうことでより良い結果を受け取ることができます）

```js:ChatGPT回答
function getSuggest(searchText, wordList, size) {
  return wordList
    .sort((a, b) => levenshteinDistance(searchText, a) - levenshteinDistance(searchText, b))
    .slice(0, size);
}
```

## 実行結果

こちらも問題なく動いているようです。

- ChatGPTリファクタリング前

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/473097/8132da31-74e5-6e6a-daac-5b02d9d8dd34.png)

- ChatGPTリファクタリング後

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/473097/cd827e18-1699-4027-272c-02363f227664.png)


# 質問3：データを作ってもらう

データを作るのも開発に必要な作業です。
自分で作るのもが面倒なので、ChatGPTに作ってもらいます。


## 質問内容

```:質問内容
初代ポケモンの配列作れる？まさか、作れないわけ無いよねぇ(^^
```

## ChatGPTの回答

完璧なデータは、作れませんでした。
`ゴローニャ`が`ゴローンダ`になっていますが、テストするデータとしては十分です。
（ほかにも間違いがあるかもしれないので探してみてください）

```js:ChatGPT回答
const firstGenPokemon = [
  "フシギダネ", "フシギソウ", "フシギバナ", "ヒトカゲ", "リザード", "リザードン", 
  "ゼニガメ", "カメール", "カメックス", "キャタピー", "トランセル", "バタフリー", 
  "ビードル", "コクーン", "スピアー", "ポッポ", "ピジョン", "ピジョット", 
  "コラッタ", "ラッタ", "オニスズメ", "オニドリル", "アーボ", "アーボック", 
  "ピカチュウ", "ライチュウ", "サンド", "サンドパン", "ニドラン♀", "ニドリーナ", 
  "ニドクイン", "ニドラン♂", "ニドリーノ", "ニドキング", "ピッピ", "ピクシー", 
  "ロコン", "キュウコン", "プリン", "プクリン", "ズバット", "ゴルバット", 
  "ナゾノクサ", "クサイハナ", "ラフレシア", "パラス", "パラセクト", "コンパン", 
  "モルフォン", "ディグダ", "ダグトリオ", "ニャース", "ペルシアン", "コダック", 
  "ゴルダック", "マンキー", "オコリザル", "ガーディ", "ウインディ", "ニョロモ", 
  "ニョロゾ", "ニョロボン", "ケーシィ", "ユンゲラー", "フーディン", "ワンリキー", 
  "ゴーリキー", "カイリキー", "マダツボミ", "ウツドン", "ウツボット", "メノクラゲ", 
  "ドククラゲ", "イシツブテ", "ゴローン", "ゴローンダ", "ポニータ", "ギャロップ", 
  "ヤドン", "ヤドラン", "コイル", "レアコイル", "カモネギ", "ドードー", 
  "ドードリオ", "パウワウ", "ジュゴン", "ベトベター", "ベトベトン", "シェルダー", 
  "パルシェン", "ゴース", "ゴースト", "ゲンガー", "イワーク", "スリープ", 
  "スリーパー", "クラブ", "キングラー", "ビリリダマ", "マルマイン", "タマタマ", 
  "ナッシー", "カラカラ", "ガラガラ", "サワムラー", "エビワラー", "ベロリンガ", 
  "ドガース", "マタドガス", "サイホーン", "サイドン", "ラッキー", "モンジャラ", 
  "ガルーラ", "タッツー", "シードラ", "トサキント", "アズマオウ", "ヒトデマン", 
  "スターミー", "バリヤード", "ストライク", "ルージュラ", "エレブー", "ブーバー", 
  "カイロス", "ケンタロス", "コイキング", "ギャラドス", "ラプラス", "メタモン", 
  "イーブイ", "シャワーズ", "サンダース", "ブースター", "ポリゴン", "オムナイト", 
  "オムスター", "カブト", "カブトプス", "プテラ", "カビゴン", "フリーザー", 
  "サンダー", "ファイヤー", "ミニリュウ", "ハクリュー", "カイリュー", "ミュウツー", 
  "ミュウ"
];
```


# 完成

それでは、いままでのコードと組み合わせて実行してみましょう

左のデータがより類似度が高い文字列です。
間違った名前で実行してみましたが、想定されたポケモンが出てきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/473097/9029736f-96cf-184b-eaea-9afcf02d2f30.png)


# ChatGPTを利用したプログラミングの利点と欠点

### 利点

- **コーディングが速い**
今回作った「レーベシュタイン距離を使った文字の類似度を計測」するプログラムは、自分で作ろうとすると、結構時間がかかると思いますが、ChatGPTを利用することで数秒で作ってくれます。

- **新しい知識が増える**
コーディング方法やメソッドの利用方法など、自分が想像しているものと違う手法で実装させるので、新しい知識が増える


### 欠点

- **ブラックボックス化してしまう**
書いてもらったプログラムが理解ができない場合、ブラックボックス化してしまう可能性があり、今後のメンテナンスなどに影響が出ます。実際に組み込む場合は責任を持って、組み込みましょう。


- **間違っている可能性がある**
基本的なことですが、書いてもらったプログラムが間違っていることがあります（結構多いです）。ChatGPTを利用するのであれば、それを見極められるレベルの技術者になってほしいです。



# おわりに

今回は、ChatGPTに質問しながら、「レーベンシュタイン距離を使い、類似度の高い文字を返却するプログラム」が作成できました。

ChatGPTの使い方としては、特にリファクタリングがおすすめです。

次回は、今回作ったプログラムを実際にアプリに組み込みする内容で記事を書きたいと思います。
ここまで読んで頂きありがとうございました。

--------------------

2023/12/07追記 Reactで検索のサジェスト作りました!

https://qiita.com/ishi720/items/a026b8b29b31e0ef6ee0


<!--
<details><summary>おまけ</summary>
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/473097/fd851b58-5a68-7237-4ca4-1708770edc88.png)
</details>
-->