【问题标题】:Equivalent of Cons Pattern from F# in Rust for StringsRust for Strings 中来自 F# 的 Cons 模式的等价物
【发布时间】:2016-01-08 00:33:25
【问题描述】:

我正在通过实现我的一个小型 F# sn-p 来试验 Rust。

我正处于想要解构一串字符的地步。这是 F#:

 let rec internalCheck acc = function
    | w :: tail when Char.IsWhiteSpace(w) -> 
        internalCheck acc tail
    | other
    | matches
    | here

..可以这样调用:internalCheck [] "String here" 其中:: 运算符表示右侧是“列表的其余部分”。

所以我查看了 Rust 文档,其中有一些解构向量的示例,如下所示:

let v = vec![1,2,3];

match v {
    [] => ...
    [first, second, ..rest] => ...
}

..等等。然而,这现在落后于slice_patterns 功能门。我尝试了类似的方法:

match input.chars() {
    [w, ..] => ...
}

这告诉我功能门需要使用非稳定版本。

所以我下载了multirust 并安装了我能找到的最新的夜间版本 (2016-01-05),当我终于让 slice_patterns 功能正常工作时......我遇到了关于语法和“休息”的无尽错误(在上面的例子)是不允许的。

那么,有没有一种等效的方法来解构字符串,在 Rust 中利用 :: 类似的功能......基本上我想将 1 个字符与一个守卫匹配,并在后面的表达式中使用“其他所有内容”。

如果答案是“不,没有”,这是完全可以接受的。我当然无法在任何地方在线找到很多此类示例,并且切片模式匹配在功能列表中似乎并不高。

(如果我在 Rust 文档中遗漏了什么,我会很乐意删除这个问题)

【问题讨论】:

  • 似乎对我有用:is.gd/h9U1SI。也许语法已经改变?仍然值得一个完整的答案。
  • @ArtemGr 是的,抱歉,这似乎有效。但是我似乎无法让它在字符串上工作。这是问题的重点(如果不清楚 - 我应该改写吗?)。
  • 顺便说一下 @ArtemGr,感谢您提供游乐场链接 - 我不知道 Rust 有这样的游乐场(竖起大拇指)。
  • is.gd/WgkdSw 怎么样?游乐场似乎有一个临时问题,但在我的工作站上打印“匹配... 1匹配... 2”。

标签: rust tail


【解决方案1】:

您可以将模式匹配与byte 切片一起使用:

#![feature(slice_patterns)]

fn internal_check(acc: &[u8]) -> bool {
    match acc {
        &[b'-', ref tail..] => internal_check(tail),
        &[ch, ref tail..] if (ch as char).is_whitespace() => internal_check(tail),
        &[] => true,
        _ => false,
    }
}

fn main() {
    for s in ["foo", "bar", "   ", " - "].iter() {
        println!("text '{}', checks? {}", s, internal_check(s.as_bytes()));
    }
}

您可以将它与 char 切片一起使用(其中 char 是 Unicode 标量值):

#![feature(slice_patterns)]

fn internal_check(acc: &[char]) -> bool {
    match acc {
        &['-', ref tail..] => internal_check(tail),
        &[ch, ref tail..] if ch.is_whitespace() => internal_check(tail),
        &[] => true,
        _ => false,
    }
}

fn main() {
    for s in ["foo", "bar", "   ", " - "].iter() {
        println!("text '{}', checks? {}",
                 s, internal_check(&s.chars().collect::<Vec<char>>()));
    }
}

但到目前为止,它不适用于&amp;str(生成E0308)。我认为这是最好的,因为 &amp;str 既不存在也不存在,它是一个 byte 切片,但 Rust 试图保证它是有效的 UTF-8 并试图提醒你使用 &amp;str unicode 序列和字符而不是字节。因此,为了有效地匹配 &amp;str,我们必须显式使用 as_bytes 方法,本质上是告诉 Rust“我们知道自己在做什么”。

无论如何,那是我的阅读。如果您想更深入地挖掘 Rust 编译器的源代码,您可以从 issue 1844 开始并浏览那里链接的提交和问题。

基本上我想将 1 个字符与警卫匹配并使用“一切 else" 在后面的表达式中。

如果您只想匹配 单个 字符,则使用 chars 迭代器获取字符并匹配字符本身可能比转换整个 UTF-8 &amp;str 更好进入&amp;[char] 切片。例如,使用chars 迭代器,您不必为字符数组分配内存。

fn internal_check(acc: &str) -> bool {
    for ch in acc.chars() {
        match ch {
            '-' => (),
            ch if ch.is_whitespace() => (),
            _ => return false,
        }
    }
    return true;
}

fn main() {
    for s in ["foo", "bar", "   ", " - "].iter() {
        println!("text '{}', checks? {}", s, internal_check(s));
    }
}

您还可以使用 chars 迭代器在 Unicode 标量值边界上拆分 &amp;str

fn internal_check(acc: &str) -> bool {
    let mut chars = acc.chars();
    match chars.next() {
        Some('-') => internal_check(chars.as_str()),
        Some(ch) if ch.is_whitespace() => internal_check(chars.as_str()),
        None => true,
        _ => false,
    }
}

fn main() {
    for s in ["foo", "bar", "   ", " - "].iter() {
        println!("text '{}', checks? {}", s, internal_check(s));
    }
}

但请记住,到目前为止,Rust 不保证将尾递归函数优化为循环。 (尾调用优化本来是该语言的一个受欢迎的补充,但由于与 LLVM 相关的困难,目前尚未实现)。

【讨论】:

  • 这太棒了!我真的很感激你花时间把这些放在一起。你的样本中有 100 件我可以学习的东西(包括工作的和非工作的)。 RE 贡献.. 我当然会在未来完成这项任务.. 一旦我对 Rust 更熟悉了。再次感谢!我真的很感激!
  • 不客气! )此外,我已经在本地检查了此答案中的所有样本,并且所有样本都应该可以正常工作。如果其中一些对您不起作用,请给我留言。
  • 由于 LLVM 相关的困难 — 也因为 TCO 使堆栈跟踪看起来不一样。 IIRC,关键字 become 保留用于选择未来的 TCO。
  • 我意识到我从未接受你的回答@ArtemGr。对此深表歉意。
  • @SimonWhitehead NP! )
【解决方案2】:

我不相信。切片模式也不太可能适用于此,因为模式的“和其余”部分进入数组模式内部,这意味着以某种方式放置所述模式里面是一个字符串,这意味着一个不存在的转义机制。

此外,Rust 没有适当的“连接”运算符,它拥有的运算符不能参与解构。所以,我不会屏住呼吸。

【讨论】:

  • 很公平。这是我在 F# 中喜欢的一个功能,所以我确实希望在 Rust 中它是可能的。尽管如此,它不会阻止我调查 Rust ......所以谢谢 :)
【解决方案3】:

只是要在这里发布......它似乎做我想要的。作为一个简单的测试,这将只打印字符串中的每个字符,但在找到空白字符时打印Found a whitespace character。它递归地执行此操作并解构字节向量。我必须向@ArtemGr 大喊一声,他给了我灵感,让我研究使用字节,看看这是否解决了我在使用chars 时遇到的编译器问题。

毫无疑问,我还没有意识到内存问题(复制/分配等;尤其是在 String 实例周围)...但我会在深入研究内部工作时解决这些问题锈。它也可能比它需要的更冗长..这正是我经过一番修改后得到的。

#![feature(slice_patterns)]

use std::iter::FromIterator;
use std::vec::Vec;

fn main() {
    process("Hello world!".to_string());
}

fn process(input: String) {
    match input.as_bytes() {
        &[c, ref _rest..] if (c as char).is_whitespace() => { println!("Found a whitespace character"); process(string_from_rest(_rest)) },
        &[c, ref _rest..] => { println!("{}", c as char); process(string_from_rest(_rest)) },
        _ => ()
    }
}

fn string_from_rest(rest: &[u8]) -> String {
    String::from_utf8(Vec::from_iter(rest.iter().cloned())).unwrap()
}

输出:

H
e
l
l
o
Found a whitespace character
w
o
r
l
d
!

显然,作为针对单个字节的测试(并且在重建字符串时仅考虑可能的 UTF-8 字符),它不适用于宽字符。我的实际用例只需要 ASCII 空间中的字符.. 所以现在就足够了。

我想,为了处理更广泛的字符,Rust 模式匹配需要能够键入强制(我不相信你现在可以这样做?),因为 Chars&lt;'T&gt; 迭代器似乎被推断为 &amp;[_] .尽管在我的其他尝试中,这可能只是我对 Rust 语言的不成熟。

【讨论】:

  • +1 去那里。除非您只使用 ASCII,否则我认为您需要处理 &amp;[char] 切片,然后唯一的瓶颈就是从 &amp;str 有效地获取它。我会为此使用堆栈分配的数组 (crates.io/crates/stack),但这是另一回事。
猜你喜欢
  • 2016-10-31
  • 1970-01-01
  • 2010-12-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-10
  • 1970-01-01
  • 2012-09-04
相关资源
最近更新 更多