首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在适当的地方洗牌

如何在适当的地方洗牌
EN

Stack Overflow用户
提问于 2015-01-07 09:43:51
回答 2查看 1.7K关注 0票数 4

我想把一根绳子挪到铁锈里去,但我好像错过了什么。修复可能是微不足道的..。

代码语言:javascript
复制
use std::rand::{Rng, thread_rng};

fn main() {
    // I want to shuffle this string...
    let mut value: String = "SomeValue".to_string();
    let mut bytes = value.as_bytes();
    let mut slice: &mut [u8] = bytes.as_mut_slice();

    thread_rng().shuffle(slice);

    println!("{}", value); 
}

我得到的错误是

代码语言:javascript
复制
<anon>:8:36: 8:41 error: cannot borrow immutable dereference of `&`-pointer `*bytes` as mutable
<anon>:8         let mut slice: &mut [u8] = bytes.as_mut_slice();
                                            ^~~~~

我读过关于String::as_mut_vec()的文章,但是它不安全,所以我不想使用它。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2015-01-07 10:27:19

没有很好的方法来做到这一点,部分是由于字符串的UTF-8编码的性质,另一部分是由于Unicode和文本的固有属性。

至少有三层东西可以在UTF-8字符串中被洗牌:

  • 原始字节
  • 编码码点
  • 图形素

洗牌原始字节很可能会将无效的UTF-8字符串作为输出,除非字符串完全是ASCII。非ASCII字符被编码为多个字节的特殊序列,对这些字符进行洗牌肯定不会使它们在结束时按正确的顺序排列。因此,洗牌字节往往是不好的。

洗牌代码点(char in Rust)更有意义,但仍然存在“特殊序列”的概念,在这里,所谓的combining characters可以分层到一个字母上,添加对话框等(例如,ä这样的字母可以写成a + U+0308,表示the diaeresis的代码点)。因此,洗牌字符不会给出一个无效的UTF-8字符串,但它可能会打破这些代码点序列,并提供无意义的输出。

这就引出了图形素:构成单个可见字符的代码点序列(就像ä一样,当作为一个或两个代码点编写时仍然是一个单一的字形素)。这将给出最可靠的合理答案。

然后,一旦您决定要洗牌,就可以制定洗牌策略:

  • 如果保证字符串是纯粹的ASCII,用.shuffle对字节进行洗牌是合理的(按照ASCII的假设,这与其他的相同)
  • 否则,就没有标准的就地操作方法,人们可以将元素作为迭代器(.chars()表示代码点,.graphemes(true)表示图形),将它们放入带有.collect::<Vec<_>>()的向量中,对向量进行洗牌,然后用.iter().map(|x| *x).collect::<String>()将所有内容收集回一个新的String中。

处理代码点和图形符号的困难在于UTF-8不能将它们编码为固定的宽度,因此无法将随机代码点/图形素插入到其他地方,或者以其他方式有效地交换两个元素.而不只是将所有内容解码为外部Vec

不到位是不幸的,但约束是困难的。

(如果您的字符串保证是ASCII,那么使用ascii提供的ascii提供的类型将是在类型级别保持清晰的好方法。)

作为这三种不同之处的一个例子,请看一看:

代码语言:javascript
复制
fn main() {
    let s = "U͍̤͕̜̲̼̜n̹͉̭͜ͅi̷̪c̠͍̖̻o̸̯̖de̮̻͍̤";
    println!("bytes: {}", s.bytes().count());
    println!("chars: {}", s.chars().count());
    println!("graphemes: {}", s.graphemes(true).count());
}

它打印:

代码语言:javascript
复制
bytes: 57
chars: 32
graphemes: 7

(Generate your own演示了将多个组合字符放在一个字母上。)

票数 12
EN

Stack Overflow用户

发布于 2015-01-07 10:12:33

我也是生锈的初学者,但是关于:

代码语言:javascript
复制
fn main() {
    // I want to shuffle this string...
    let value = "SomeValue".to_string();
    let mut bytes = value.into_bytes();

    bytes[0] = bytes[1]; // Shuffle takes place.. sorry but std::rand::thread_rng is not available in the Rust installed on my current machine.

    match String::from_utf8(bytes) { // Should not copy the contents according to documentation.
        Ok(s) => println!("{}", s),
        _ => println!("Error occurred!")
    }
}

还请记住,Rust默认的字符串编码是UTF-8时摆弄字节序列。;)

这是一个很好的建议,带我到下面的解决方案,谢谢!

代码语言:javascript
复制
use std::rand::{Rng, thread_rng};

fn main() {
    // I want to shuffle this string...
    let value: String = "SomeValue".to_string();
    let mut bytes = value.into_bytes();

    thread_rng().shuffle(&mut *bytes.as_mut_slice());

    match String::from_utf8(bytes) { // Should not copy the contents according to documentation.
        Ok(s) => println!("{}", s),
        _ => println!("Error occurred!")
    }
}

(ad9e75938 2015-01-05 00:26:28 +0000)

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/27816488

复制
相关文章

相似问题

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