Taste of Tech Topics

Acroquest Technology株式会社のエンジニアが書く技術ブログ

ChatGPTは、難解なNode.jsの処理を解釈して、Pythonに移植できるのか?

こんにちは、igaです。
先日の連休で、あるコンテンツの聖地巡礼をして英気を養ってきました!


英気を養ったところで、「Node.jsからPythonソースコードを移植する」ということが必要になりました。
元のNode.jsのコードでPythonには存在しない書き方をしていて、そのままPythonに書き直すのが難しいため、ChatGPTに助けてもらって移植を行ってみよう、と考えました。

今回のポイント

変換にあたって、Node.jsで変数の値をインクリメントする「index++」という記述が、Pythonには存在しません。
同じように変数の値をインクリメントする場合、Pythonでは「index += 1」という記述にする必要があります。

それで今回のソースコードですが、関数の引数を指定するところでインクリメントの、しかもやや複雑な記述が存在していました。

num = this.#translateInt16(this.#hexArr[index++], this.#hexArr[index++]);

上記のような実装で、「translateInt16」メソッドの引数に、「hexArr[index++]」という配列の変数が指定されています。
さらに、1行で2つも「index++」というインクリメントの指定があって、実際にどのような値が渡されるのか、分かるでしょうか?
これは、人間がぱっと考えても難しいですよね?

#そもそも、このような難解な実装をするな、と思われる人も多いと思いますが、私もそう思いました(笑)

移植するNode.jsのソースコード

冒頭のコードを含むNode.jsの移植ソースコードは、以下のような内容です。
(解説するために内容は単純化しています。そのため、変換処理でのエラーチェックなども省略しています。)

const MESSAGE_HEADER = 0x80;

const MESSAGE_STATUS = 0x01
const MESSAGE_VALUE = 0x02;

class MessageData {
    // Message Status
    status = 0;

    // Message Value
    value = 0;
};

class MessageDataParser {
    // input data is hex string
    #payload = '';
    #arrLength = 0;

    // translated hex arry from input string
    #hexArr = [];

    // Constructor
    constructor(payload) {
        this.#payload = payload;
    }
    
    // translate lettle-endian
    #translateInt16(a, b) {
        return a + (b << 8);
    }

    #parseMessage(index, messageData) {
        switch(this.#hexArr[index++]){
            case MESSAGE_STATUS:
                messageData.status = this.#translateInt16(this.#hexArr[index++], this.#hexArr[index++]);
                break;
            case MESSAGE_VALUE:
                messageData.value = this.#translateInt16(this.#hexArr[index++], this.#hexArr[index++]);
                break;
            default:
                // ignore unknown message.
                index += 2;
                break;
        }

        return index;
    }

    parse() {
        let msg = new MessageData();
        let arrayIndex = 0;

        // for debug
        // console.info('Parse payload.: payload=' + this.#payload);

        const payloadLength = this.#payload.length;

        // parse hex string to array
        this.#arrLength = payloadLength / 2;
        for (let i = 0; i < this.#arrLength; i++) {
            this.#hexArr.push(parseInt(this.#payload.substring(i * 2, i * 2 + 2), 16)); //parse hex
        }

        if (this.#hexArr[arrayIndex++] == MESSAGE_HEADER) {
            do {
                arrayIndex = this.#parseMessage(arrayIndex, msg);
            }while(arrayIndex < this.#arrLength)
        }

        return msg;
    }
}

module.exports = MessageDataParser

変換元のソースコードは、メッセージのデシリアライズを行うものになります。
今回デシリアライズする、メッセージの仕様は以下の通りです。

メッセージ仕様

ChatGPTにNode.jsからPythonに変換してもらう

以下のプロンプトで、ChatGPT(今回は、GPT-4を使用しました)にNode.jsからPythonに変換してもらいます。

ChatGPTが変換してくれた結果が、以下のコードになります。

class MessageData:
    def __init__(self):
        self.status = 0
        self.value = 0


class MessageDataParser:
    MESSAGE_HEADER = 0x80
    MESSAGE_STATUS = 0x01
    MESSAGE_VALUE = 0x02

    def __init__(self, payload):
        self._payload = payload
        self._hex_arr = []
        self._arr_length = 0

    def _translate_int16(self, a, b):
        return a + (b << 8)

    def _parse_message(self, index, message_data):
        code = self._hex_arr[index]
        index += 1
        if code == self.MESSAGE_STATUS:
            message_data.status = self._translate_int16(self._hex_arr[index], self._hex_arr[index + 1])
            index += 2
        elif code == self.MESSAGE_VALUE:
            message_data.value = self._translate_int16(self._hex_arr[index], self._hex_arr[index + 1])
            index += 2
        else:
            # ignore unknown message.
            index += 2
        return index

    def parse(self):
        msg = MessageData()
        array_index = 0
        self._arr_length = len(self._payload) // 2
        for i in range(self._arr_length):
            self._hex_arr.append(int(self._payload[i * 2:i * 2 + 2], 16))  # parse hex
        if self._hex_arr[array_index] == self.MESSAGE_HEADER:
            array_index += 1
            while array_index < self._arr_length:
                array_index = self._parse_message(array_index, msg)
        return msg
変換結果

さて冒頭でも説明したインクリメントの記述部分はどうなったでしょうか?

Node.jsでの記述:

messageData.status = this.#translateInt16(this.#hexArr[index++], this.#hexArr[index++]);
  • 1つ目の引数にはhexArr[index]の値を渡す
  • 2つ目の引数にはhexArr[index+1]を渡す

が変換後も再現されていれば成功です。
また関数の呼び出しが行われた後は、indexの値が2増加しているはずです。

Pythonでの記述:

message_data.status = self._translate_int16(self._hex_arr[index], self._hex_arr[index + 1])
index += 2

Node.jsでの処理内容と同じことがPythonに変換した結果に反映されています。
インクリメントの記述を、単純に「index += 1」で置き換えるのではなく、Node.jsで記述された意味と同じになるようになっています。

  • 1つ目の引数に_hex_arr[index]の値を渡す
  • 2つ目の引数に_hex_arr[index+1]を渡す

そして、関数の呼び出しを行った後でindexの値を2増加させるため「index += 2」と変換しています。
完璧ですね…!

変換されたコードの動作確認

変換した処理の内容は正しそうですが、念のためPythonコードが正しく動くのか、pytestでテストコードを書いて確認します。

import pytest
from src.dataparser import MessageDataParser

def test_parse_status_only():
    parser = MessageDataParser('80010203')
    data = parser.parse()
    assert data.status == 0x302
    assert data.value == 0

def test_parse_value_only():
    parser = MessageDataParser('80020203')
    data = parser.parse()
    assert data.status == 0
    assert data.value == 0x302

def test_parse_unknown_code():
    parser = MessageDataParser('80030203')
    data = parser.parse()
    assert data.status == 0
    assert data.value == 0

def test_parse_status_and_value():
    parser = MessageDataParser('80010203020204')
    data = parser.parse()
    assert data.status == 0x302
    assert data.value == 0x402

def test_parse_no_header():
    parser = MessageDataParser('010203')
    data = parser.parse()
    assert data.status == 0
    assert data.value == 0

def test_parse_skip_unknown():
    parser = MessageDataParser('80010203040706020204')
    data = parser.parse()
    assert data.status == 0x302
    assert data.value == 0x402

テストを実行するとすべてPASSEDになり、正常に動作することが確認できました。

pytest実行結果

まとめ

今回、ChatGPTにプログラム言語の変換をやってもらいましたが、変換先の言語で書けない処理であっても適切に変換してくれました。

単純に変換元と変換先で同じ記述が使えない場合に、対応する記述に置き換えるのではなく、記述した意味を把握して変換を行っているように見えます。
これはかなり強力なので、プログラムの移植にChatGPTを活用するのは有用だと思います。

「小企業が実践したノウハウを展開!中小企業でChatGPTどう活かすのか?」 近日開催します!

20230726204731