СИСТЕМНО ПРОГРАМИРАНЕ

C Да ръждясва с помощта на ChatGPT

Компилация от източник към източник

Процесът на трансформиране на изходния код от един език на друг се нарича Транспилация. Инструментът, използван за този процес, се нарича транспилер. В тази статия ще използваме ChatGPT като транспилер за трансформиране на C код в Rust код, изследвайки неговите възможности и ограничения.

Едно от най-значимите срещнати предизвикателства е ограниченият размер на бързото въвеждане. Обикновено ChatGPT-3.5 поддържа до 4096 токена (токените са сложна концепция, която не означава непременно „дума“). В тази статия ще разгледаме само програми, които могат да се поберат в един ChatGPT подкана.

Методика

За този експеримент ние подбрахме набор от предизвикателства за кодиране, получени от Leetcode. След това изпратихме всеки код на ChatGPT и го помолихме да преведе кода. След това се опитахме да компилираме и изпълним преведения код. Ако компилацията е неуспешна, помолихме ChatGPT да коригира кода до три пъти (N = 3).

След представяне на получените резултати предоставяме ръчно изпълнение на методиката. Ние също така показваме скрипта, използван за автоматизиране на процеса чрез API на ChatGPT. И накрая, отбелязваме, че досега сме използвали само ChatGPT-3.5.

Пример за методология

В примера по-долу имаме малка програма, която изчислява „Най-дългата нарастваща подпоследователност“ (обяснение може да се намери тук). C кодът е по-долу:

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

/* Trace the LIS using an array and use binary search, O(nlogn) */
int lengthOfLIS(int* nums, int numsSize) {
    if (nums == NULL || numsSize == 0) return 0;
    int *lis = (int *)malloc(numsSize * sizeof(int));
    lis[0] = nums[0];
    int len = 1;
    int i;
    for (i = 1; i < numsSize; i++) {
        if (nums[i] > lis[len - 1]) {
            lis[len++] = nums[i];
        }
        else {
            int l = 0, r = len - 1;
            while (l < r) {
                int m = l + (r - l) / 2;
                if (lis[m] >= nums[i]) {
                    r = m;
                }
                else {
                    l = m + 1;
                }
            }
            lis[l] = nums[i];
        }
    }
    free(lis);
    return len;
}

int main() {
    int nums0[] = { 10, 9, 2, 5, 3, 7, 101, 18 };
    int nums1[] = { 1, 0, 1, 1 };
    assert(lengthOfLIS(nums0, sizeof(nums0) / sizeof(nums0[0])) == 4);
    assert(lengthOfLIS(nums1, sizeof(nums1) / sizeof(nums1[0])) == 2);

    printf("all tests passed!\n");

    return 0;
}

Използвахме тази проста подкана, за да поискаме от ChatGPT да го преведе („…“ представлява останалата част от кода):

Translate this code from C to Rust:
#include <stdio.h>
#include <stdlib.h>
...

Първоначалният генериран код не се компилира успешно при първия опит:

// Add in the correction: use std::convert::TryInto; 
fn length_of_lis(nums: &[i32]) -> i32 {
    let nums_size = nums.len();
    if nums_size == 0 {
        return 0;
    }
    let mut lis = vec![0; nums_size];
    lis[0] = nums[0];
    let mut len = 1;
    for i in 1..nums_size {
        if nums[i] > lis[len - 1] {
            lis[len] = nums[i];
            len += 1;
        } else {
            let (mut l, mut r) = (0, len - 1);
            while l < r {
                let m = l + (r - l) / 2;
                if lis[m] >= nums[i] {
                    r = m;
                } else {
                    l = m + 1;
                }
            }
            lis[l] = nums[i];
        }
    }
    len // Correct it to: len.try_into().unwrap()
}

fn main() {
    let nums0 = [10, 9, 2, 5, 3, 7, 101, 18];
    let nums1 = [1, 0, 1, 1];
    assert_eq!(length_of_lis(&nums0), 4);
    assert_eq!(length_of_lis(&nums1), 2);
    println!("all tests passed!");
}

Получената грешка е:

Проблемът е, че се очаква върнатата стойност да бъде i32,, докато на len е даден типът по подразбиране usize (64 бита). Предоставяйки тази грешка обратно на ChatGPT, получаваме нова версия на програмата, съдържаща корекцията!

Корекцията се състои в добавяне на един ред и модифициране на друг, за да се предаде върнатата стойност. Вижте коментаритев предишния код на rust.

Автоматизиране на процеса

Създадохме скрипт на Python, който автоматизира този процес. Скриптът е проектиран да обработва директория с файлове с изходен код на C и да се опитва да ги преведе на Rust с помощта на API на ChatGPT.

Той преминава през всеки C файл, превежда кода в Rust, компилира и изпълнява преведения код с помощта на Rust Docker. Ако има някакви грешки при компилиране, той ще се опита да поправи кода, като предостави съобщението за грешка на ChatGPT до 3 пъти: коригиращ цикъл.

Скриптът записва успеха на всяка стъпка, включително превод, компилация и изпълнение, както и броя на направените опити за коригиране на кода. Той също така взема предвид дали кодът се „вписва“ в ограниченията за въвеждане на модела (когато кодът е твърде голям, ChatGPT API излъчва изключение).

Накрая, той генерира таблица, обобщаваща резултатите и съхранява преведения код на Rust, регистрационни файлове със съобщения за грешки и подробности за резултатите в отделни файлове за всеки обработен C файл.

Резултати

Резюме

Общият брой тествани програми е 159. Имаше само пет програми, които бяха твърде големи, за да се поберат в един ред; по този начин ще ги игнорираме. Ще разгледаме само останалите 154 кандидатури. От тях успяхме да транспилираме, компилираме и стартираме 82 приложения, което представлява 51,5% успеваемост.

Шестдесет програми бяха правилно транспилирани при първия изстрел, което представлява 37,7% от общия набор. Коригирани са само 22 програми, което представлява 13,8% от целия набор. От всички неуспешни транспилации (94) това представлява приблизително 23%. Това показва, че коригиращият цикъл не е ефективен и може да изисква допълнително подобрение.

Ефективността на транспилация на ChatGPT

Вариация. След като анализирахме резултатите, забелязахме, че те не винаги са последователни. Както е показано по-долу, ето три други изпълнения на същия експеримент:

Продължителност. Ние също така измерваме времето, необходимо за всяка заявка към ChatGPT, независимо дали е заявка за транспилиране или за коригиране на кода. Средно времето за всяка заявка е около 30 секунди.

Заключение

Тази статия предостави кратък преглед на възможностите за използване на ChatGPT като транспилер. От текущата публично достъпна версия, 3.5, все още има нужда от човешка намеса за коригиране на някои грешки, които може да са били генерирани по време на процеса на транспилация.

Освен това някои може да твърдят, че транспилацията не е добре направена, тъй като кодът все още запазва структура на кодиране в стил C. Тази точка обаче е извън обхвата на тази статия, тъй като разглеждаме само транспилация, независимо от получения стил на код.

И накрая, ние ви каним да покажете своята подкрепа, като пляскате на тази статия и ни следвате за по-проницателно съдържание относно програмирането, технологиите и AI. Вашето насърчение ще ни вдъхнови да продължим да изследваме и споделяме знанията си.

Въпроси и отговори

Въпрос 1:

Parceval: Чудя се дали програмите все още изпълняват същата задача. Опитахте ли да направите някои тестове на транспилираните програми?

Отговор 1:

Страхотен въпрос!

Предистория: Има 2 вида приложения. Приложението Type1 има „asserts“ за проверка на резултата, което прави изпълнението неуспешно, ако assert-ът е неуспешен. Ние проверяваме върнатата стойност. (Наистина приемаме, че „твърденията“ са преведени правилно)

Приложенията от Type2 нямат assert и просто извеждат резултата. За тях проверих ръчно резултатите, но след въпроса ви автоматизирах процеса. И… Наистина имаше едно приложение, което отпечата грешен резултат: приложение „224.c“

Ще актуализира статията съответно!

Кодиране на ниво нагоре

Благодарим ви, че сте част от нашата общност! Преди да тръгнеш:

🚀👉 Присъединете се към колектива за таланти Level Up и намерете невероятна работа