2016-09-11 3 views
3

때로는 프로그램이 사용자에게 동적 값을 포함하는 메시지를 구사하기위한 다양한 방법이 있습니다. 예 :형식 문자열을 무작위로 선택하는 방법

  • "{} 분 남음"
  • "{} 분 이내에 끝내야합니다."

메시지의 일부가 단순 접두사 또는 접미사로 사용 된 것은 아닙니다. 동적 언어에서는 문자열 서식 지정의 논리적 작업처럼 보였습니다.

pub fn h(x: usize) -> String { 
    rand::sample(rand::thread_rng(), vec![ 
     format!("{} minutes remain.", x), 
     format!("Hurry up; only {} minutes left to finish.", x), 
     format!("Haste advisable; time ends in {}.", x), 
     /* (insert many more elements here) */ 
    ], 1).first().unwrap_or(format!("{}", x)) 
} 

은 다음과 같습니다 :

반복성은 바람직하지 않다 미디어

(예 여유 채널) 같은 것을 사용하여 출력에 각각 최종 String을 생산하는 많은 다른 어법이있다

  • 지루한에 저자는 매번 format!(/*...*/, x)을 타이핑하는 것과 관련하여
  • 하나의 가능성이 선택되기 전에 모든 가능성이 완전히 생성되기 때문에 낭비가 많고 다른 것들은 폐기됩니다.

이러한 단점을 피할 수있는 방법이 있습니까?

형식 문자열을 컴파일 타임으로 평가하지 않았 으면 무작위로 선택된 &'static str (정적 슬라이스에서)을 format!에 전달하는 함수가 선호되는 솔루션이었을 것입니다.

+2

관련 항목 : [어떻게 동적 형식 문자열을 형식으로 사용할 수 있습니까? 매크로?] (http://stackoverflow.com/q/32572486/155423). – Shepmaster

+2

무작위로 숫자를 생성하고 'match'를 사용하여 형식을 지정할 문자열을 선택했다고 생각하십니까? 모든 문자열을 생성 한 다음 멀리 던지는 것을 피할 수 있습니다. – Aurora0001

답변

5

녹 기능은 기능을 정의하는 기능을 지원합니다. 우리는 함수 포인터 조각을 만들 수 있고, rand::sample 중 하나를 선택하고 선택한 함수를 호출 할 수 있습니다.

extern crate rand; 

use rand::Rng; 

pub fn h(x: usize) -> String { 
    fn f0(x: usize) -> String { 
     format!("{} minutes remain.", x) 
    } 

    fn f1(x: usize) -> String { 
     format!("Hurry up; only {} minutes left to finish.", x) 
    } 

    fn f2(x: usize) -> String { 
     format!("Haste advisable; time ends in {}.", x) 
    } 

    let formats: &[fn(usize) -> String] = &[f0, f1, f2]; 
    (*rand::thread_rng().choose(formats).unwrap())(x) 
} 

이것은 "지루한"측면이 아니라 원래 솔루션의 "낭비적인"측면을 해결합니다. 매크로를 사용하여 반복의 양을 줄일 수 있습니다. 함수 내에 정의 된 매크로는 해당 함수에 대해서도 로컬이라는 점에 유의하십시오! 이 매크로는 Rust의 위생적인 ​​매크로를 사용하여 f이라는 여러 함수를 정의하므로 매크로를 사용할 때 각 함수의 이름을 제공 할 필요가 없습니다.

extern crate rand; 

use rand::Rng; 

pub fn h(x: usize) -> String { 
    macro_rules! messages { 
     ($($fmtstr:tt,)*) => { 
      &[$({ 
       fn f(x: usize) -> String { 
        format!($fmtstr, x) 
       } 
       f 
      }),*] 
     } 
    } 

    let formats: &[fn(usize) -> String] = messages!(
     "{} minutes remain.", 
     "Hurry up; only {} minutes left to finish.", 
     "Haste advisable; time ends in {}.", 
    ); 
    (*rand::thread_rng().choose(formats).unwrap())(x) 
} 
+0

이것은 Rust의 매크로 기능을 좀 더 자세히 살펴야하는 이유를 보여줍니다. 감사! – mmirate

4

나의 제안은 불필요한 계산을 피하기 위해 일치를 사용하고 컴팩트 가능한 한 코드를 유지하는 것입니다 :

use rand::{thread_rng, Rng}; 

let mut rng = thread_rng(); 
let code: u8 = rng.gen_range(0, 5); 
let time = 5; 
let response = match code { 
    0 => format!("Running out of time! {} seconds left", time), 
    1 => format!("Quick! {} seconds left", time), 
    2 => format!("Hurry, there are {} seconds left", time), 
    3 => format!("Faster! {} seconds left", time), 
    4 => format!("Only {} seconds left", time), 
    _ => unreachable!() 
}; 

(Playground link)

인정 하듯이, 말 그대로 숫자에 맞게 조금 추한 , 그러나 아마도 당신이 그것을 얻을 수있는 가장 짧은 것입니다. 다수의 스트링을 생성

+1

내 유지 보수 관심사는'match'의 선택 수와'gen_range'의 수로 나뉩니다. – Shepmaster

+1

그래, 짜증나지만 피하기 힘들다. 나는 상한값을'max_choices'로 설정하고 반박 불가능한 패턴을'n> = max_choices'라면'n '으로 대체함으로써이 문제를 해결하려고 노력했지만, 컴파일러는 그것이 완전하다는 것을 알 수 없다. (http://play.integer32.com/?gist=de8cf938ebc0bb5571bc3b406324627a)). – Aurora0001

2

클로저 (또는 함수 포인터)를 사용하여 회피 할 직선적이다

extern crate rand; 

use rand::Rng; 

pub fn h(x: usize) -> String { 
    let messages: &[&Fn() -> String] = &[ 
     &|| format!("{} minutes remain.", x), 
     &|| format!("Hurry up; only {} minutes left to finish.", x), 
     &|| format!("Haste advisable; time ends in {}.", x), 
    ]; 
    let default_message = || format!("{}", x); 

    rand::thread_rng().choose(messages).unwrap_or(&&(&default_message as &Fn()->String))() 
} 

fn main() { 
    println!("{}", h(1)); 
} 

주 :

  1. choose 대신 하나 개의 값 sample.
  2. Vec은 필요하지 않습니다. 배열 괜찮을 것입니다.

"아름다운"측면을 개선하지는 않습니다.

extern crate rand; 

macro_rules! messages { 
    {$default: expr, $($msg: expr,)*} => { 
     use rand::Rng; 

     let messages: &[&Fn() -> String] = &[ 
      $(&|| $msg),* 
     ]; 
     let default_message = || $default; 

     rand::thread_rng().choose(messages).unwrap_or(&&(&default_message as &Fn() -> String))() 
    } 
} 

pub fn h(x: usize) -> String { 
    messages! { 
     format!("{}", x), 
     format!("{} minutes remain.", x), 
     format!("Hurry up; only {} minutes left to finish.", x), 
     format!("Haste advisable; time ends in {}.", x), 
    } 
} 

fn main() { 
    println!("{}", h(1)); 
} 

참고 : 매크로 천역 제거 할 수 있습니다 매크로는 적어도 하나 개의 인수가 필요합니다

  1. 을; 이것은 기본 메시지로 사용됩니다.
관련 문제