文本到数字转换

问题描述 投票:0回答:2

我正在尝试创建一个 Rust 版本的这个问题的公认解决方案,即将“258”这样的字符串转换为 258。

我创建了下面的代码来匹配给定的伪代码,但不知何故它无法正确获取成百上千的代码。例如,我的示例字符串返回 158 而不是 258。我错过了什么?

let _units = vec![ "zero", "one", "two", "three", "four", "five", "six", "seven", 
  "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", 
  "sixteen", "seventeen", "eighteen", "nineteen", ];    

let mut numbers: Vec<(&str,u32)> = _units.iter().enumerate().map(|(idx, n)| (*n, idx as u32)).collect();

let _tens = vec!["", "", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"];

let mut tens: Vec<(&str,u32)> = _tens.iter().enumerate().map(|(idx, n)| (*n, (idx * 10) as u32)).collect();

let _scales = vec!["hundred", "thousand", "million", "billion"];

let base:i32 = 1000;

let mut scales: Vec<(&str,u32)> = _scales.iter().enumerate().map(|(idx, n)| (*n, base.pow(idx as u32) as u32)).collect();

numbers.append(&mut tens);
numbers.append(&mut scales);

use std::collections::HashMap;

fn text_to_int(textnum: &str, numwords: HashMap<&str, u32>) -> u32 {
  let mut result = 0;
  let mut prior = 0;

  for word in textnum.split(" ") {
    let value = numwords[word];

    if prior == 0 {
      prior = value;
    } else if prior > value {
      prior += value;
    } else {
      prior *= value;
    };

    if value > 100 && prior != 0 {
      result += prior;
      prior = 0;
    };
  }
  return result + prior;
}

let numwords: HashMap<_, _> = numbers.into_iter().collect();

let textnum = "two hundred fifty eight thousand";

println!("{:?}", text_to_int(textnum, numwords));

返回 158000。我做错了什么?

rust
2个回答
2
投票

你的问题出在这条线上

let base:i32 = 1000;

let mut scales: Vec<(&str,u32)> = _scales.iter().enumerate().map(|(idx, n)| (*n, base.pow(idx as u32) as u32)).collect();

因为“百”给你的是 1 而不是 100

使用您的算法,您无法生成正确的数字

scales

"hundred" -> 100 -> 10^2
"thousand" -> 1_000 -> 10^3
"million" -> 1_000_000 -> 10^6
"billion" -> 1_000_000_000 -> 10^9

解决此问题的简单方法是手动添加“百”

let _scales = vec!["thousand", "million", "billion"];
let base:i32 = 1000;
let mut scales: Vec<(&str,u32)> = _scales.iter().enumerate().map(|(idx, n)| (*n, base.pow(idx as u32 + 1) as u32)).collect();

numbers.append(&mut tens);
numbers.append(&mut scales);
numbers.push(("hundred", 100));

[编辑] 解决这个问题的另一种方法是使用

FromStr
,这将为您带来
parse()
函数的优势

为此,您可以使用以下代码

首先创建一个用于保存值的自定义类型,例如

u32

#[derive(Debug)]
struct Number<T>(T);

如果你也暗示

Display
,那就太好了(可选)

impl<T: Display> Display for Number<T> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.0)
    }
}

现在创建文本数字图

const BASIC: [(&'static str, u32); 32] = [
    ("zero", 0), 
    ("one", 1), 
    ("two", 2), 
    ("three", 3), 
    ("four", 4), 
    ("five", 5), 
    ("six", 6), 
    ("seven", 7), 
    ("eight", 8), 
    ("nine", 9), 
    ("ten", 10),
    ("eleven", 11),
    ("twelve", 12),
    ("thirteen", 13),
    ("fourteen", 14),
    ("fifteen", 15), 
    ("sixteen", 16),
    ("seventeen", 17),
    ("eighteen", 18),
    ("nineteen", 19),
    ("twenty", 20),
    ("thirty", 30),
    ("forty", 40),
    ("fifty", 50),
    ("sixty", 60),
    ("seventy", 70),
    ("eighty", 80),
    ("ninety", 90),
    ("hundred", 100),
    ("thousand", 1000),
    ("million", 1000000),
    ("billion", 1000000000),
];

现在只需将您的

text_to_int
功能代码放入
FromStr
函数中,如下所示

impl FromStr for Number<u32> {
    type Err = ();

    fn from_str(textnum: &str) -> Result<Self, Self::Err> {
        let textnum = textnum.to_lowercase();
        let numwords: HashMap<_, _> = BASIC.into_iter().collect();
        let mut result = 0u32;
        let mut prior = 0u32;

        for word in textnum.split(" ") {
            let value = numwords[word].into();

            if prior == 0 {
                prior = value;
            } 
            else if prior > value {
                prior += value;
            } 
            else {
                prior *= value;
            }

            if value > 100 && prior != 0 {
                result += prior;
                prior = 0;
            }
        }
        Ok(Number(result + prior))
    }
}

现在你可以像这样解析每个

&str

fn main() {
    let num: Number<u32> = "two hundred fifty eight thousand".parse().unwrap();
    println!("{}", num);
}

-1
投票

您遇到的问题源于您的逻辑如何处理“百”和“千”的值。具体来说,在处理“一百”这个词时,它应该将当前值(例如“二”= 2)乘以 100,但您的代码过早地添加或重置值,导致结果不正确。

以下是需要调整的详细信息:

乘以刻度:遇到“百”时,应将当前值(先前值)乘以100,而不是立即重置先前值。相反,请存储此值以便使用后面的较小数字(例如“五十”和“八”)进一步计算。

累加大尺度:当遇到像“千”这样的较大尺度时,您应该在按千值缩放之前添加累加结果(例如“258”)。

纠正逻辑: 逐步处理输入字符串中的每个单词,确保遇到“百”或“千”时,在移动到下一组数字之前正确缩放当前累加值。

修复示例:修改出现“百”或“千”等比例时处理先验和结果的方式:

fn text_to_int(textnum: &str, numwords: HashMap<&str, u32>) -> u32 {

令 mut 结果 = 0; 让 mut 先验 = 0;

for word in textnum.split(" ") { 让 value = numwords[word];

if value == 100 {
  prior *= value;
} else if value >= 1000 {
  result += prior * value;
  prior = 0;
} else {
  prior += value;
}

}

结果+之前 }

说明: 仅当遇到“百”时先验才会相乘。 对于像“千”这样的大值,先验值会在缩放后添加到结果中,然后重置以进行进一步计算。 您可以进一步测试和完善此逻辑以适应更复杂的输入。如需更高级的 SEO 工具,请访问 SEO Studio Tools

© www.soinside.com 2019 - 2024. All rights reserved.