程序员可以放心了,ChatGPT只能写95%的代码! - 网络动向论坛 - 吾爱微网

程序员可以放心了,ChatGPT只能写95%的代码!

领域特定语言(DSL)可能很多人都知道,就是针对特定领域的,一个简单的“小语言”,例如HTML就是针对Web编程的一个DSL,SQL是针对数据库操作的一个DSL。

DSL可以很复杂,也包含if, else,while等逻辑控制,也可以很简单,我选这个例子就是税务领域的一个简单例子:

// 定义税率区间
tax_bracket 1
  range: 0 - 15000
  rate: 5%

tax_bracket 2
  range: 15001 - 45000
  rate: 10%

tax_bracket 3
  range: 45001 - 120000
  rate: 20%

tax_bracket 4
  range: 120001 - 200000
  rate: 30%

tax_bracket 5
  //注:就是这个不同寻常的格式引发了Bug
  range: 200001 and above 
  rate: 40%

// 定义抵免额
deduction Standard
  amount: 5000

deduction Health_Insurance
  amount: 2000

deduction Retirement_Savings
  amount: 3000
有了这些术语,业务人员就可以使用了

// 计算应纳税额
income: 80000
deductions: Standard, Health_Insurance, Retirement_Savings
calculate_tax

我把这个需求直接抛给GPT-4, 让它用Java语言给我实现了,下面是我和它的对话:

注:交互内容较长,文末有总结

GPT-4 :为了实现这个DSL,我将首先创建一个TaxBracket类来表示税率区间,然后创建一个Deduction类来表示抵免额。最后,我将创建一个TaxCalculator类来实现计算应纳税额的逻辑。以下是一个简化的Java实现:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

class TaxBracket {
    int id;
    int lowerBound;
    int upperBound;
    double rate;

    public TaxBracket(int id, int lowerBound, int upperBound, double rate) {
        this.id = id;
        this.lowerBound = lowerBound;
        this.upperBound = upperBound;
        this.rate = rate;
    }
}

class Deduction {
    String name;
    int amount;


    public Deduction(String name, int amount) {
        this.name = name;
        this.amount = amount;
    }
}

class TaxCalculator {
    List
   
     taxBrackets;
    List
    
      deductions;
    int income;

    public TaxCalculator(int income, List
     
       taxBrackets, List
      
        deductions) {
        this.income = income;
        this.taxBrackets = taxBrackets;
        this.deductions = deductions;
    }

    public double calculateTax() {
        int taxableIncome = income;
        for (Deduction deduction : deductions) {
            taxableIncome -= deduction.amount;
        }

        double tax = 0;
        for (TaxBracket bracket : taxBrackets) {
            if (taxableIncome <= bracket.upperBound) {
                tax += (taxableIncome - bracket.lowerBound ) * bracket.rate;
                break;
            } else {
                tax += (bracket.upperBound - bracket.lowerBound ) * bracket.rate;
            }
        }
        return tax;
    }
}

public class Main {
    public static void main(String[] args) {
        List
       
         taxBrackets = new ArrayList<>(Arrays.asList( new TaxBracket(1, 0, 15000, 0.05), new TaxBracket(2, 15001, 45000, 0.10), new TaxBracket(3, 45001, 120000, 0.20), new TaxBracket(4, 120001, 200000, 0.30), new TaxBracket(5, 200001, Integer.MAX_VALUE, 0.40) )); List
        
          deductions = new ArrayList<>(Arrays.asList( new Deduction("Standard", 5000), new Deduction("Health_Insurance", 2000), new Deduction("Retirement_Savings", 3000) )); int income = 80000; TaxCalculator calculator = new TaxCalculator(income, taxBrackets, deductions); double tax = calculator.calculateTax(); System.out.println("应纳税额: " + tax); } }

这个实现定义了TaxBracket和Deduction类,并创建了一个TaxCalculator类来计算应纳税额。主方法中初始化了税率区间和抵免额,并使用这些信息计算了一个给定收入的应纳税额。

注:它似乎理解了这个DSL是用来计算税率的,类名,方法名也比较合理

刘欣:不不不,应该解析一个DSL文件,形成TaxBracket和Deduction,然后再计算税率,你这里少了一个解析器

GPT-4 :抱歉,我明白你的需求了。这里是一个使用Java编写的解析器,用于解析DSL文件并计算税率:

首先,我们需要创建一个名为DSLParser的类来解析DSL文件:

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

class DSLParser {
    private final List
   
     taxBrackets = new ArrayList<>();
    private final List
    
      deductions = new ArrayList<>();
    private int income;

    public DSLParser(String filePath) {
        try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
            String line;
            while ((line = reader.readLine()) != null) {
                parseLine(line.trim());
            }
        } catch (IOException e) {
            System.err.println("Error reading file: " + e.getMessage());
        }
    }

    private void parseLine(String line) {
        if (line.startsWith("tax_bracket")) {
            int id = Integer.parseInt(line.split(" ")[1]);
            int lowerBound = 0;
            int upperBound = 0;
            double rate = 0;

            for (int i = 0; i < 2; i++) {
                line = getNextLine();


                if (line.startsWith("range:")) {
                    String[] range = line.substring(6).trim().split(" - ");
                    lowerBound = Integer.parseInt(range[0]);
                    if (range[1].equals("and above")) {
                        upperBound = Integer.MAX_VALUE;
                    } else {
                        upperBound = Integer.parseInt(range[1]);
                    }
                } else if (line.startsWith("rate:")) {
                    rate = Double.parseDouble(line.substring(5).trim().replace("%", "")) / 100;
                }
            }
            taxBrackets.add(new TaxBracket(id, lowerBound, upperBound, rate));
        } else if (line.startsWith("deduction")) {
            String name = line.split(" ")[1];
            int amount = 0;

            line = getNextLine();
            if (line.startsWith("amount:")) {
                amount = Integer.parseInt(line.substring(7).trim());
            }

            deductions.add(new Deduction(name, amount));
        } else if (line.startsWith("income:")) {
            income = Integer.parseInt(line.substring(7).trim());
        } else if (line.equals("calculate_tax")) {
            // Do nothing, just a marker to calculate tax
        }
    }

    private String getNextLine() {
        // Read the next line from the file, or return null if end of file is reached
    }

    public List
     
       getTaxBrackets() {
        return taxBrackets;
    }

    public List
      
        getDeductions() {
        return deductions;
    }

    public int getIncome() {
        return income;
    }
}

接下来,我们需要修改Main类,以便从DSL文件读取税收信息并计算税率:

public class Main {
    public static void main(String[] args) {
        if (args.length != 1) {
            System.out.println("Usage: java Main 
   
    ");
            return;
        }

        DSLParser parser = new DSLParser(args[0]);
        List
    
      taxBrackets = parser.getTaxBrackets();
        List
     
       deductions = parser.getDeductions();
        int income = parser.getIncome();

        TaxCalculator calculator = new TaxCalculator(income, taxBrackets, deductions);
        double tax = calculator.calculateTax();
        System.out.println("应纳

注:这里没输出完,就断掉了。

刘欣:继续

GPT-4 :

税额: " + tax);
}
}

现在,这个实现已经包括了解析DSL文件的功能。你可以将上述代码保存为Java源文件(例如`TaxCalculation.java`),并将你的DSL文件保存为一个文本文件(例如`tax.dsl`)。然后使用以下命令编译并运行代码:

javac TaxCalculation.java
java Main tax.dsl

这将从tax.dsl文件读取税率区间和抵免额信息,并根据收入计算税率。请注意,在使用此方法之前,你可能需要稍微调整DSLParser类中的getNextLine方法,以便正确读取文件的下一行。你可以使用BufferedReader对象的readLine方法实现这一点。

刘欣: DSLParser中getNextLine方法没有实现!

GPT-4 :抱歉,这是我的疏忽。请在DSLParser类中添加一个BufferedReader类型的成员变量reader,并在构造函数中初始化它。然后在getNextLine方法中实现读取下一行的逻辑。以下是修改后的DSLParser类:

class DSLParser {
    private final List
   
     taxBrackets = new ArrayList<>();
    private final List
    
      deductions = new ArrayList<>();
    private int income;
    private BufferedReader reader;

    public DSLParser(String filePath) {
        try {
            this.reader = new BufferedReader(new FileReader(filePath));
            String line;
            while ((line = reader.readLine()) != null) {
                parseLine(line.trim());
            }
            reader.close();
        } catch (IOException e) {
            System.err.println("Error reading file: " + e.getMessage());
        }
    }

    private void parseLine(String line) {
        // ... (same as before)
    }

    private String getNextLine() {
        try {
            return reader.readLine();
        } catch (IOException e) {
            System.err.println("Error reading next line: " + e.getMessage());
            return null;
        }
    }

    // ... (getTaxBrackets, getDeductions, and getIncome methods)
}

现在,DSLParser类已包含一个reader成员变量,用于在getNextLine方法中读取文件的下一行。这应该使解析器能够正确解析DSL文件并计算税率。

注:到目前为止,GPT-4把所有的代码都生成了,用时不到5分钟!可以说是神速了,让我从头开始构思,然后一行行敲出代码,恐怕得四五十分钟吧

我把它复制出来,在VS Code中运行,发现了一个问题,它没有处理文本前后的空格。

刘欣:在parseLine方法中,你好像没有考虑文件中的一行内容前后有空格的问题

GPT-4 :您是对的,我之前的实现没有考虑到行内容前后可能存在的空格。在解析每一行时,我们可以使用trim()方法来去除前后的空格。以下是修改后的DSLParser类中的parseLine方法:

private void parseLine(String line) {
    line = line.trim();
    if (line.startsWith("tax_bracket")) {
        // ... (same as before)
    } else if (line.startsWith("deduction")) {
        // ... (same as before)
    } else if (line.startsWith("income:")) {
        income = Integer.parseInt(line.substring(7).trim());
    } else if (line.equals("calculate_tax")) {
        // Do nothing, just a marker to calculate tax
    }
}

现在,我们在处理每一行时都会去掉前后的空格,这样就可以更准确地解析含有前后空格的行内容。这将提高解析器的健壮性,使其能够更好地处理不同格式的DSL文件。

注:其实GPT-4只改了一处代码,其他地方还没改。于是我建议它:

刘欣:能否在getNextLine方法中就做trim呢?

GPT-4 : 当然可以。您可以在getNextLine方法中直接对读取到的行内容进行trim()操作。这样,在调用getNextLine方法时,就可以确保返回的行内容已经去除了前后的空格。以下是修改后的getNextLine方法:

private String getNextLine() {
    try {
        String line = reader.readLine();
        if (line != null) {
            return line.trim();
        }
    } catch (IOException e) {
        System.err.println("Error reading next line: " + e.getMessage());
    }
    return null;
}

在这个修改后的版本中,我们在getNextLine方法内部对读取到的行内容使用了trim()方法。这意味着在解析文件时,无论是通过getNextLine方法还是在parseLine方法中读取的行内容,都已经去除了前后的空格。

注:到目前为止,还一切顺利,但是很快就发生了一件让人头大的问题:GPT-4在处理range: 200001 and above 这样格式的时候出错,因为一般的格式是45001 - 120000, 120001 - 200000 ,都是数字,这里出现一个 and above 文字,它没注意到。

程序在运行时出错,我把错误直接扔给它:

刘欣:代码在运行时出错:

Exception in thread "main" java.lang.NumberFormatException: For input string: "200001 and above"
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.lang.Integer.parseInt(Integer.java:580)
at java.lang.Integer.parseInt(Integer.java:615)
at DSLParser.parseLine(App.java:94)
at DSLParser.
   
    (App.java:73)
at App.main(App.java:150)

GPT- 4 :抱歉给您带来了困扰。这个问题是由于在解析tax_bracket区间的上界时没有正确处理"and above"的情况导致的。以下是修复后的parseLine方法中处理tax_bracket部分的代码:代码略。

注:GPT-4虽然意识到了问题所在,但是它的修改方案总是有问题,反复了两三次也修不好。最后我提示它:

刘欣:在这个字符串中 range: 200001 and above , 根据就没有“-”, 所以line.substring(6).trim().split(" - ") 这行代码是没用的

GPT-4: 您是对的,我再次犯了错误。为了处理"and above"这种情况,我们应该根据"and above"字符串来分割范围。以下是修复后的parseLine方法中处理tax_bracket部分的代码:

private void parseLine(String line) {
    if (line.startsWith("tax_bracket")) {
        int id = Integer.parseInt(line.split(" ")[1]);
        int lowerBound = 0;
        int upperBound = 0;
        double rate = 0;

        for (int i = 0; i < 2; i++) {
            line = getNextLine();

            if (line.startsWith("range:")) {
                String rangeString = line.substring(6).trim();
                if (rangeString.contains("and above")) {
                    String[] range = rangeString.split(" and above");
                    lowerBound = Integer.parseInt(range[0]);
                    upperBound = Integer.MAX_VALUE;
                } else {
                    String[] range = rangeString.split(" - ");
                    lowerBound = Integer.parseInt(range[0]);
                    upperBound = Integer.parseInt(range[1]);
                }
            } else if (line.startsWith("rate:")) {
                rate = Double.parseDouble(line.substring(5).trim().replace("%", "")) / 100;
            }
        }

        taxBrackets.add(new TaxBracket(id, lowerBound, upperBound, rate));
    } else if (line.startsWith("deduction")) {
        // ... (same as before)
    } else if (line.startsWith("income:")) {
        income = Integer.parseInt(line.substring(7).trim());
    } else if (line.equals("calculate_tax")) {
        // Do nothing, just a marker to calculate tax
    }
}

现在,在解析税率区间时,我们首先检查范围字符串是否包含"and above"。如果包含,则根据"and above"分割范围,并将上界设置为Integer.MAX_VALUE。否则,我们使用" - "分隔符来分割范围。这次修改应该可以正确处理税率区间,并避免NumberFormatException异常。

注:这一次,它终于做对了。

总结一下:

ChatGPT实在是太牛了!

它不但可以理解需求,生成代码,更让人震撼的是可以根据要求不断地微调代码,完善功能,修复Bug。

过程完全用自然语言交互,这种能力我没有在之前的任何工具中看到过,说它是革命性的技术完全不为过。

但是,我们也看到了它忘记(?)实现一个方法,特别是一个Bug一直搞不定,直到我明确地指出来。

正如球友@楊_宵夜所说:ChatGPT能以极快的速度生成95%的可运行代码,但是剩下的5%依然依靠程序员的功力。

在ChatGPT的冲击下,程序员的价值依然是很大的,依然是不可或缺的的。

但是,会用熟练使用ChatGPT的程序员,可以用自然语言快速生成代码,效率提升十倍甚至百倍,这将对不使用ChatGPT的程序员构成降维打击。

请登录后发表评论

    请登录后查看回复内容