Construir código a partir del lenguaje natural

Completado

Una de las funcionalidades de los modelos de Azure OpenAI es generar código a partir de mensajes de lenguaje natural. Las tareas pueden oscilar entre un sencillo comando de una línea hasta una aplicación completa. Los modelos de IA también pueden editar y actualizar el código proporcionado o las respuestas anteriores para completar la tarea solicitada.

Los ejemplos de esta unidad usan gpt-35-turbo.

Escribir funciones

Los modelos de Azure OpenAI pueden crear funciones y aplicaciones en varios lenguajes con tan solo describir aquello que quiere. Por ejemplo, dada la solicitud write a function for binary search in python, es probable que reciba una respuesta con la función y una explicación del código.

Sure, here's an example implementation of binary search algorithm in Python:

def binary_search(array, target):  
    low = 0  
    high = len(array) - 1  
  
    while low <= high:  
        mid = (low + high) // 2  
        if array[mid] == target:  
            return mid  
        elif array[mid] < target:  
            low = mid + 1  
        else:  
            high = mid - 1  
  
    return -1  
 
This function takes in two arguments - an array of integers and a target integer that we want to search
for in the array. The function initializes two pointers - low and high - to the first and last indices
of the array, respectively.

Inside the while loop, the function calculates the middle index using integer division (//). If the 
middle element is equal to the target, the function returns the index of the middle element. If the 
middle element is less than the target, we update the low pointer to mid + 1. Otherwise, we update the 
high pointer to mid - 1. We continue these steps until we find the target or the pointers cross each 
other (i.e., low > high).

If the target is not found in the array, the function returns -1.

Note that the binary search algorithm assumes that the input array is sorted in ascending order. If 
the array is not sorted, the algorithm may not return the correct result.

Se pueden solicitar no solo funciones, sino también una serie de funciones o una aplicación completa. La respuesta proporciona un punto de partida óptimo para la tarea que intenta lograr.

Cambio del lenguaje de codificación

Si tiene código en un lenguaje pero lo necesita en otro, Azure OpenAI puede traducirlo automáticamente. Por ejemplo, supongamos que tiene la función print_squares en Python.

def print_squares(n):  
    for i in range(1, n+1):  
        print(i**2)   

Si proporciona ese código, junto con el mensaje convert this code to C#, probablemente obtenga algo similar a la función siguiente.

public void PrintSquares(int n)  
{  
    for (int i = 1; i <= n; i++)  
    {  
        Console.WriteLine(i * i);  
    }  
}  

Tenga en cuenta que el modelo ha entendido las diferencias entre cómo se definen las funciones y los bucles for en los distintos lenguajes (incluidos los modificadores de acceso en C#) y ha cambiado el código en consecuencia.

Entender código desconocido

Los modelos de Azure OpenAI son útiles para comprender código que no tiene sentido o que pueda estar en un lenguaje con el que no esté familiarizado. Por ejemplo, supongamos que se le ha asignado la siguiente función (en un lenguaje de codificación ficticio) y que no la entiende.

fn qzplv(n: i32, m: i32) -> i32 {
    if n == 0 {
        return m + 1;
    } else if m == 0 {
        return qzplv(n - 1, 1);
    } else {
        return qzplv(n - 1, qzplv(n, m - 1));
    }
}

Cuando se le proporciona al modelo, junto con el mensaje could you explain what this code is doing?, se obtiene una respuesta similar a la siguiente:

Sure, I'd be happy to explain what this code is doing!

This code is implementing the Ackermann function, which is a well-known mathematical function that 
grows very quickly in terms of computation time as its inputs increase. The function takes two integer 
arguments (n and m) and returns another integer.

Here's how the function works:
- If n is equal to 0, the function returns m + 1.
- If m is equal to 0, the function calls itself recursively with n - 1 and 1 as arguments.
- Otherwise, the function calls itself recursively with n - 1 and the result of calling itself with n 
and m - 1 as arguments.

The function is implemented in a way that is easy to understand and follow, but it has a very high 
computational complexity. In fact, it grows so quickly that it can't be computed for values of n and m 
greater than about 4 or 5.

La combinación de la explicación y la traducción a un lenguaje con el que esté familiarizado hace que esta funcionalidad sea aún más útil.

Completar código y ayudar en el proceso de desarrollo

Azure OpenAI puede ayudar a los desarrolladores en tareas comunes de desarrollo de software, como escribir pruebas unitarias, completar código parcial, comentar código y generar documentación. El uso de la ayuda de inteligencia artificial puede permitir a los desarrolladores dedicar más tiempo a tareas complejas de programación y de resolución de problemas.

Los modelos de Azure OpenAI pueden generar código en función de comentarios, nombres de función y código escrito parcialmente. Cuanto más contexto pueda proporcionar al modelo, probablemente más precisa será la respuesta.

Por ejemplo, cuando se da el mensaje siguiente:

complete the following function  
"""  
# calculate the average of the numbers in an array, but only if they're even  
def 

El modelo toma los comentarios y el inicio de la definición de función y lo completa desde cualquier contexto que tenga.

El funcionamiento es similar al proporcionar código parcial al modelo, sin comentarios. Tome el mensaje siguiente, por ejemplo.

complete the following code  
"""  
def func1(n)
  if n==0:
complete the following code  
"""  
public int Func1(int n) {  
    if (n == 0) { 

El modelo de IA hace todo lo posible por completar la función con la finalización más probable de la función.

def func1(n):  
    if n == 0:  
        return 1  
    else:  
        return n * func1(n-1)  
public int Func1(int n) {  
    if (n == 0) {  
        return 1;  
    } else {  
        return n * Func1(n-1);  
    }  
}

La respuesta también puede incluir una explicación en lenguaje natural de lo que hace esa función. En este caso, respondió con una implementación recursiva de la función factorial.

Escribir pruebas unitarias

Los modelos de Azure OpenAI pueden generar pruebas unitarias para las funciones que escribe a fin de ayudarle a que el código sea más sólido. Tome como ejemplo la función de búsqueda binaria.

def binary_search(arr, target):
    left, right = 0, len(arr) - 1
    while left <= right:
        mid = (left + right) // 2
        if arr[mid] == target:
            return mid
        elif arr[mid] < target:
            left = mid + 1
        else:
            right = mid - 1
    return -1
public static int BinarySearch(int[] arr, int target)
{
    int left = 0;
    int right = arr.Length - 1;

    while (left <= right)
    {
        int mid = (left + right) / 2;

        if (arr[mid] == target)
        {
            return mid;
        }
        else if (arr[mid] < target)
        {
            left = mid + 1;
        }
        else
        {
            right = mid - 1;
        }
    }

    return -1;
}

Proporcione esa función junto con el mensaje write three unit tests for this function y obtendrá una respuesta similar a la siguiente.

def test_binary_search():  
    # Test case 1: Target element is in the middle of the array  
    arr1 = [1, 2, 3, 4, 5, 6]  
    target1 = 4  
    assert binary_search(arr1, target1) == 3  

    # Test case 2: Target element is at the beginning of the array  
    arr2 = [1, 2, 3, 4, 5, 6]  
    target2 = 1  
    assert binary_search(arr2, target2) == 0  

    # Test case 3: Target element is not in the array  
    arr3 = [1, 2, 3, 4, 5, 6]  
    target3 = 7  
    assert binary_search(arr3, target3) == -1
[Test]  
public void TestBinarySearch1()  
{  
    int[] arr = { 1, 2, 3, 4, 5 };  
    int target = 3;  
    int expected = 2;  
    int result = BinarySearch(arr, target);  
    Assert.AreEqual(expected, result);  
}  

[Test]  
public void TestBinarySearch2()  
{  
    int[] arr = { 10, 20, 30, 40, 50 };  
    int target = 35;  
    int expected = -1;  
    int result = BinarySearch(arr, target);  
    Assert.AreEqual(expected, result);  
}  

[Test]  
public void TestBinarySearch3()  
{  
    int[] arr = { 3, 6, 9, 12, 15 };  
    int target = 6;  
    int expected = 1;  
    int result = BinarySearch(arr, target);  
    Assert.AreEqual(expected, result);  
}  

Esta funcionalidad es aún más útil si especifica los casos que quiere incluir en el mensaje, lo que reduce el tiempo que tarda en escribir pruebas unitarias para el código.

Adición de comentarios y generación de documentación

Para mejorar aún más el código, los modelos de IA pueden agregar comentarios y documentación. Tome la función siguiente como ejemplo, aunque puede ser un poco difícil entenderla al leer por primera vez sin ningún comentario de código.

def permutations(lst):  
    if len(lst) == 0:  
        return []  
    elif len(lst) == 1:  
        return [lst]  
    else:  
        result = []  
        for i in range(len(lst)):  
            temp = lst[i]  
            remaining = lst[:i] + lst[i+1:]  
            for p in permutations(remaining):  
                result.append([temp] + p)  
        return result  
public static List<List<int>> Permutations(List<int> lst)  
{  
    if (lst.Count == 0)  
    {  
        return new List<List<int>>();  
    }  
    else if (lst.Count == 1)  
    {  
        return new List<List<int>> { lst };  
    }  
    else  
    {  
        List<List<int>> result = new List<List<int>>();  
        for (int i = 0; i < lst.Count; i++)  
        {  
            int temp = lst[i];  
            List<int> remaining = new List<int>(lst);  
            remaining.RemoveAt(i);  
            foreach (List<int> p in Permutations(remaining))  
            {  
                List<int> permutation = new List<int> { temp };  
                permutation.AddRange(p);  
                result.Add(permutation);  
            }  
        }  
        return result;  
    }  
}  

Proporcione esa función al modelo, junto con un mensaje en el que se solicite que agregue comentarios al código, y obtendrá una respuesta similar a la siguiente.

def permutations(lst):  
    # Check if the input list is empty  
    if len(lst) == 0:  
        return []  
    # Check if the input list has only one element  
    elif len(lst) == 1:  
        return [lst]  
    else:  
        # Initialize an empty list to store permutations  
        result = []  
        # Iterate through each element of the input list  
        for i in range(len(lst)):  
            # Remove the current element from the list and store it in a variable  
            temp = lst[i]  
            remaining = lst[:i] + lst[i+1:]  
            # Recursively call the function on the remaining list  
            for p in permutations(remaining):  
                # Append the current element to the beginning of each permutation and add it to the result list  
                result.append([temp] + p)  
        # Return the final list of permutations  
        return result  
public static List<List<int>> Permutations(List<int> lst)  
{  
    // Check if the input list is empty  
    if (lst.Count == 0)  
    {  
        return new List<List<int>>();  
    }  
    // Check if the input list has only one element  
    else if (lst.Count == 1)  
    {  
        return new List<List<int>> { lst };  
    }  
    else  
    {  
        // Initialize an empty list to store permutations  
        List<List<int>> result = new List<List<int>>();  
        // Iterate through each element of the input list  
        for (int i = 0; i < lst.Count; i++)  
        {  
            // Remove the current element from the list and store it in a variable  
            int temp = lst[i];  
            List<int> remaining = new List<int>(lst);  
            remaining.RemoveAt(i);  
            // Recursively call the function on the remaining list  
            foreach (List<int> p in Permutations(remaining))  
            {  
                // Append the current element to the beginning of each permutation and add it to the result list  
                List<int> permutation = new List<int> { temp };  
                permutation.AddRange(p);  
                result.Add(permutation);  
            }  
        }  
        // Return the final list of permutations  
        return result;  
    }  
}  

Si lo lleva más allá y solicita documentación, OpenAI agregará una descripción, incluidas las entradas y salidas.

Corrección de errores y mejora del código

A veces, los desarrolladores pueden escribir código que, si bien funciona, puede mejorarse mediante la corrección de los errores, la sintaxis, el rendimiento o la modularidad. Los modelos de Azure OpenAI pueden ayudar a identificar formas de mejora y proporcionar sugerencias sobre cómo escribir mejor código.

Corrección de errores en el código

Los modelos de Azure OpenAI pueden ayudar a corregir errores del código mediante el análisis de este y la sugerencia de cambios que pueden corregir el problema. Esto puede ayudar a los desarrolladores a identificar y a resolver los errores de forma más rápida y eficaz.

Por ejemplo, supongamos que tiene la función siguiente que no es adecuada para su caso.

def calculate_average(numbers):  
    total = 0  
    for i in range(len(numbers)):  
        number = numbers[i]  
        total += number  
    average = total  
    rerun averave  
public double CalculateAverage(int[] numbers)
{
    int total = 0;
    for (int i = 0; i < numbers.Length; i++)
    {
        int number = numbers[i];
        total += number;
    }
    double average = (double)total;
    rerun averave;
}

Proporcione esa función al modelo, junto con el mensaje Fix the bugs in this function, y obtendrá una respuesta con el código corregido y una explicación de qué se ha corregido.

def calculate_average(numbers):  
    total = 0  
    for i in range(len(numbers)):  
        number = numbers[i]  
        total += number  
    average = total / len(numbers)  
    return average  
public double CalculateAverage(int[] numbers)
{
    int total = 0;
    for (int i = 0; i < numbers.Length; i++)
    {
        int number = numbers[i];
        total += number;
    }
    double average = (double)total / numbers.Length;
    return average;
}
There were a few issues with the original code:

1. The variable "average" was assigned the value of "total" instead of the actual average.
2. There was a typo in the last line - "rerun" should be "return".
3. The average was not actually calculated - the corrected code divides the total by the length of 
the "numbers" list to get the average.

Mejorar el rendimiento

Aunque la escritura por parte de los desarrolladores de código puede funcionar, puede haber una forma más eficaz de realizar la tarea. A continuación, se muestra un ejemplo de una función que calcula la suma de los primeros n enteros positivos, posiblemente de forma ineficaz:

def sum_of_n(n):
    result = 0
    for i in range(1, n+1):
        result += i
    return result
public int SumOfN(int n)  
{  
    int result = 0;  
    for (int i = 1; i <= n; i++)  
    {  
        result += i;  
    }  
    return result;  
}  

Esta versión funciona correctamente, pero su complejidad temporal es O(n). Cuando se proporciona al modelo, la respuesta es:

We can improve this function it by using a mathematical formula instead.

Here's the more efficient version:
def sum_of_n(n):
    return n * (n + 1) // 2
public int SumOfN(int n)  
{  
    return n * (n + 1) / 2;  
}  

Esta versión también devuelve el resultado correcto, pero su complejidad temporal es ahora O(1), lo que hace que sea mucho más eficaz.

Refactorización de código ineficiente

Un código óptimo es menos propenso a errores y es más fácil de mantener. Los modelos de Azure OpenAI pueden guiar a los desarrolladores para ayudarles a refactorizar su código.

Considere la función siguiente.

def calculateTotalPrice(item, quantity):
    if item == 'apple':
        return quantity * 0.5
    elif item == 'banana':
        return quantity * 0.75
    elif item == 'orange':
        return quantity * 0.6
    else:
        return 0
public double CalculateTotalPrice(string item, int quantity)
{
    if (item == "apple")
    {
        return quantity * 0.5;
    }
    else if (item == "banana")
    {
        return quantity * 0.75;
    }
    else if (item == "orange")
    {
        return quantity * 0.6;
    }
    else
    {
        return 0;
    }
}

Este código calcula el precio total de un artículo determinado en función de su nombre y la cantidad. Sin embargo, el código no es modular y puede ser difícil de mantener. Cuando se proporciona al modelo la solicitud para refactorizarlo, esta es la respuesta:

item_prices = {  
    'apple': 0.5,  
    'banana': 0.75,  
    'orange': 0.6  
}  

def calculate_total_price(item, quantity):  
    price = item_prices.get(item, 0)  
    return quantity * price  
static Dictionary<string, double> itemPrices = new Dictionary<string, double>()  
{  
    {"apple", 0.5},  
    {"banana", 0.75},  
    {"orange", 0.6}  
};  

static double CalculateTotalPrice(string item, int quantity)  
{  
    double price;  
    if (!itemPrices.TryGetValue(item, out price))  
    {  
        price = 0;  
    }  
    return quantity * price;  
}  

Junto con el código, el modelo proporciona una explicación de la refactorización.

In this refactored code, we've created an object called itemPrices that maps item names to their 
prices. We've also simplified the calculateTotalPrice function by using the itemPrices object to look 
up the price of the item, rather than using a series of if/else statements. This makes the code more 
modular and easier to maintain, and also reduces the risk of introducing bugs in the future.