首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >自然语言倍增

自然语言倍增
EN

Code Review用户
提问于 2017-02-09 05:52:19
回答 2查看 260关注 0票数 5

我正在学习Java,这是一个程序的解决方案,它将读取0到99范围内的两个数字并将它们相乘。输入数字是用自然语言给出的,例如对于输入32-10,它应该返回320。我已经尽力了。请有人回顾一下,如果有的话,可以提出改进建议吗?

代码语言:javascript
复制
package assignments;
import java.util.*;
public class NaturalLanguageMultiply {
    private static final Map<String, Integer> numericValues = new HashMap<String, Integer>();
    private static final String WORD_SEPARATOR = "-";
    static {
        numericValues.put("zero", 0);
        numericValues.put("one", 1);
        numericValues.put("two", 2);
        numericValues.put("three", 3);
        numericValues.put("four", 4);
        numericValues.put("five", 5);
        numericValues.put("six", 6);
        numericValues.put("seven", 7);
        numericValues.put("eight", 8);
        numericValues.put("nine", 9);
        numericValues.put("ten", 10);
        numericValues.put("eleven", 11);
        numericValues.put("tweleve", 12);
        numericValues.put("thirteen", 13);
        numericValues.put("fourteen", 14);
        numericValues.put("fifteen", 15);
        numericValues.put("sixteen", 16);
        numericValues.put("seventeen", 17);
        numericValues.put("eighteen", 18);
        numericValues.put("nineteen", 19);
        numericValues.put("twenty", 20);
        numericValues.put("thirty", 30);
        numericValues.put("forty", 40);
        numericValues.put("fifty", 50);
        numericValues.put("sixty", 60);
        numericValues.put("seventy", 70);
        numericValues.put("eighty", 80);
        numericValues.put("ninety", 90);
    }

    private static int wordToNumber(String word) {
        String[] tokens = word.split(WORD_SEPARATOR);
        int number = 0;
        for(String token : tokens) {
            Integer numericValue = numericValues.get(token);
            if(numericValue == null) {
                throw new IllegalArgumentException("unknown token " + token + " in word " + word);
            }

            number += numericValue;
        }
        return number;
    }


    public static void main(String[] args) {

        if(args.length < 2) {
            System.out.println("argument missing");
            System.exit(1);
        }

        try {
            int firstNumber = wordToNumber(args[0]);
            int secondNumber = wordToNumber(args[1]);
            System.out.format("%d%n", firstNumber * secondNumber);
            System.exit(0); 
        } catch(Exception e) {
            System.out.println(e.getMessage());
            System.exit(1);
        }
    }
}
EN

回答 2

Code Review用户

发布于 2017-02-10 10:10:09

General

我不知道为什么你的问题至今仍未得到回答。但我想“练习”往往不太受关注,因为这个练习似乎是很基本的。别误会我。每个人都必须经历一些基本的事情。

所以关键是你找到了一个问题的解决方案。到目前为止你做得很好。但我希望你继续下去,因为这只是表面而已。下面是每个程序员必须经历的事情。

  1. 学习序列、选择和迭代理论。
  2. 训练你的算法思维,用你选择的编程语言,用1的元素解决问题,增加难度
  3. 熟悉语言机制
  4. 应用程序设计范例,例如函数式或面向对象的程式设计。
  5. 通过学习当前确定的26种设计模式,并学习在正确的情况下应用它们,将代码片段正规化。
  6. 按照每个代码片段的正确语义组织代码,学习并应用坚实的原则,指导您完成设计决策。
  7. 学习针对问题使用的语言的限制。也许在另一种语言中,您可以用另一种更优雅的方式来表达解决方案。

关键是,所提供的代码只能在第1、2和3点下进行判断。由于我不知道您深入java编程语言的深度,也许是时候应用面向对象或函数式编程范例了。

由于您的代码目前是程序性的,我建议以一种更面向对象的方式来制定代码。

建议

减少静态元素

静态修饰符并不是整体上的坏(例如常量),但是对于方法,它会阻碍您从OO机制“多态”中获益。

引入OO

尝试用对象来构造代码。从这里开始,删除除main-方法以外的所有方法的所有静态修饰符。为您的类"NaturalLanguageMultiply“引入一个构造函数。传入Array,进行验证,并将其存储在局部变量中。

不带System.exit(0)退出;

这一点都没有意义,因为如果程序结束,每个程序都将与退出代码0一起离开。这是一个不必要的声明。

异常处理

我建议只有一个异常处理的概念。如果您想通过调用System.exit(.)来促进程序的执行失败然后在程序的最后声明中这样做。在内部,您应该依赖异常。因此,您的参数验证不应该退出JVM,而应该抛出一个IllegalArgumentException。

目前,您捕获“异常”。我建议明确地捕获预期的异常。这有助于您返回可区分的退出代码。

此外,在代码中的任意位置避免System.out.println,以促进异常。这将导致冗余代码遍布各地。在你要处理的异常情况下推广它们。

提供了计算方法

引入一个名为getResult()的方法来进行乘法。

命名

正如注释中所建议的那样,正确命名事物将有助于其他人阅读您的代码。我会将常量名从"WORD_SEPARATOR“更改为"TOKEN_SEPARATOR”,并将"args“重命名为”word“。这对我来说是有意义的。你有两个词都是说出来的数字。一个单词通过这些标记的分隔符划分为令牌。也许你也可以把“单词”重命名为"spokenNumbers“,把"word”重命名为"spokenNumber“。但我想我已经办到了。

代码语言:javascript
复制
public class NaturalLanguageMultiply {
    
    
    private static final Map<String, Integer> numericValues = new HashMap<String, Integer>();

    
    private static final String TOKEN_SEPARATOR = "-";
    
    
    static {
        numericValues.put("zero", 0);
        numericValues.put("one", 1);
        numericValues.put("two", 2);
        ...
        numericValues.put("ninety", 90);
    }

    
    
    private String[] words;
    

    NaturalLanguageMultiply(String[] words) {

        if(words.length < 2) {
            throw new IllegalArgumentException("argument missing");
        }
        
        this.words = words;
        
    }
    
    
    private int wordToNumber(String word) {
        
        String[] tokens = word.split(TOKEN_SEPARATOR);
        
        int number = 0;
        
        for (String token: tokens) {
            
            Integer numericValue = numericValues.get(token);
            
            if(numericValue == null) {
                throw new IllegalArgumentException("unknown token " + token + " in word " + word);
            }

            number += numericValue;
        }
        
        return number;
    }
    

    private int getResult() {
        
        int firstNumber = wordToNumber(words[0]);
        int secondNumber = wordToNumber(words[1]);
        
        return firstNumber * secondNumber;
    }
    

    public void main(String[] args) {

        try {
            
            int result = new NaturalLanguageMultiply(args).getResult();
            
            System.out.format("%d%n", result);
            
        } catch (IllegalArgumentException e) {
            
            System.out.println(e.getMessage());
            System.exit(1);
            
        } catch (Exception e) {
            
            System.out.println(e.getMessage());
            System.exit(2);
            
        }
        
    }


}

进一步步骤

您的解决方案分析和解释传入的数据和处理计算。这最好用状态模式解释器模式来解决。这是一个先进的技术,当你感到准备好的时候,你应该想到它。

票数 4
EN

Code Review用户

发布于 2017-02-11 16:07:49

输入验证

当前的方法接受像twenty-twelve这样的字符串来表示32,这可能是不可取的。您可能希望验证第一个或第二个令牌是否可以与另一个令牌组合,例如,拒绝twenty-twelve,因为twelve不能是第二个令牌,并接受thirty-two

例如,您可以拥有一个NumberToken类:

代码语言:javascript
复制
public class NumberToken {
    private final int value;
    private final String text;
    private final Position position;

    public NumberToken(int value, String text, Position position) {
        // constructor
    }

    // getters...
}

public enum Position {
    FIRST, SECOND, BOTH;
}

Position的枚举值允许我们验证令牌如何与其他令牌组合。例如:

代码语言:javascript
复制
public NumberToken two = new NumberToken(2, "two", Position.BOTH);
public NumberToken twelve = new NumberToken(12, "twelve", Position.FIRST);
public NumberToken twenty = new NumberToken(20, "twenty", Position.FIRST);
public NumberToken thirty = new NumberToken(30, "thirty", Position.FIRST);

首先,可以进行的验证如下:

  1. Position.FIRSTPosition.SECOND必须位于各自的位置。
  2. Position.BOTH不能与另一个Position.BOTH组合。

您还应该考虑边缘情况,比如如何拒绝twelve-two (例如,Position.FIRST_ONLY for twelve之类的新值?)。

这种方法的一个优点是,使用新的基于流的处理很容易生成查找Map

代码语言:javascript
复制
private static final Map<String, NumberToken> LOOKUP = 
    Arrays.stream(one, two, /* ... */ ninety)
            .collect(Collectors.toMap(NumberToken::getText, Function.identity()));

误差输出

错误输出应该转到System.err而不是System.out

代码语言:javascript
复制
try {
    // ...
    // Below System.exit(int) is unnecessary, status will be 0 by default
    // System.exit(0); 
} catch(Exception e) {
    System.err.println(e.getMessage());
    System.exit(1);
}

泛型推理

从Java 7开始,您可以在变量声明中使用<>表示所需的泛型类型。,如下所示:

代码语言:javascript
复制
private static final Map<String, Integer> numericValues = new HashMap<>();
票数 2
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/154877

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档