2014-10-22 2 views
6

에 대한 솔루션을 구축하기 위해 기본적으로 매개 변수를 통해 초기 값이 수신되는 가변 숫자 변수를 통해 닫히는 클로저를 반환하는 함수가 필요합니다. 이 클로저를 호출 할 때마다이 캡처 된 변수가 클로저의 매개 변수 인 값만큼 증가하고 누적 된 값이 반환됩니다.변경 가능한 환경에서 클로저 반환

closures RFC을 읽고 난 후 상자에 넣지 않은 폐쇄를 반환하는 데 대한 질문 (특히 this). 나는 결국 컴파일 된 솔루션을 생각 해낼 수 있었지만 그 결과는 내가 기대했던 것이 아니다.

#![feature(unboxed_closures, unboxed_closure_sugar)] 

fn accumulator_factory(n: f64) -> Box<|&mut: f64| -> f64> { 
    let mut acc = n; 
    box |&mut: i: f64| { 
     acc += i; 
     acc 
    } 
} 

fn main() { 
    let mut acc_cl = accumulator_factory(5f64); 
    println!("{}", acc_cl.call_mut((3f64,))); 
    println!("{}", acc_cl.call_mut((3f64,))); 
} 

는 AFAIK이 고정 값에 의해 acc 환경이 가변이며 acc_cl 통화와 같은 환경의 인스턴스를 유지한다 역할 생성 포착 구조체.

그러나 인쇄 된 결과는 두 경우 모두 6이므로 수정 된 값이 지속되지 않는 것으로 보입니다. 그리고 더 혼란스러운 것은이 결과가 계산되는 방법입니다. 클로저를 실행할 때마다 acc의 초기 값은 3이고 n5입니다.

fn accumulator_factory(n: f64) -> Box<|&mut: f64| -> f64> { 
    println!("n {}", n); 
    let mut acc = n; 
    box |&mut: i: f64| { 
     acc += i; 
     acc 
    } 
} 

다음 실행이 항상 3을 반환하고 acc의 초기 값은 폐쇄 항목을 항상 0입니다 :

나는이에 발전기를 수정합니다.

이 의미론의 차이점은 버그처럼 보입니다. 하지만 그 외에도, 호출간에 환경이 재설정되는 이유는 무엇입니까?

이것은 녹슬지 않은 0.12.0으로 수행되었습니다.

+1

또 다른 접근 방법은 https://github.com/Hoverbear/rust-rosetta/blob/master/src/accumulator_factory.rs에서 확인할 수 있습니다. 녹슬지 않은 새로운 클로저는 구조체와 특성 구현을위한 설탕입니다. 이 버전은 긴 "unsugared"버전을 작성합니다 –

답변

8

클로저의 캡처 모드가 최근에 변경되었습니다. 클로저는 참조로 모든 것을 캡처하도록 편향되어 있습니다. 클로저의 가장 일반적인 사용 사례는 호출 스택 아래로 함수에 전달하므로 참조로 캡처하면 환경과보다 자연스럽게 작업 할 수 있기 때문입니다.

간혹 참조로 캡처하는 것이 제한적입니다. 예를 들어 환경이 호출 스택에 연결되어 있으므로 함수에서 이러한 클로저를 반환 할 수 없습니다. 이러한 폐쇄를 들어 당신은 폐쇄하기 전에 move 키워드를 넣어해야합니다

fn accumulator_factory(n: f64) -> Box<FnMut(f64) -> f64> { 
    println!("n: {}", n); 
    let mut acc = n; 
    Box::new(move |i: f64| { 
     acc += i; 
     acc 
    }) 
} 

fn main() { 
    let mut acc = accumulator_factory(10.0); 
    println!("{}", acc(12.0)); 
    println!("{}", acc(12.0)); 
} 

이 프로그램은 의도 한대로 작동합니다

n: 10 
22 
34 

이 두 폐쇄 종류 this RFC에 의해 보호됩니다.

+0

'acc (12.0)'대신'acc (12.0)'을 호출 할 수 있습니까? 호출 구문은 약간 압도적 인 것처럼 보입니다. –

+0

@MatthieuM. 그 이유는 오버로드 된 호출이 작동하지 않는 이유를 이해하지 못한다는 것입니다. 그것들은 또한 feature-gated이지만, 비록이 문을 들었을지라도 컴파일러는 여전히 불평합니다. –

+0

@VladimirMatveev 감사합니다. 나는이 구문의 변화에 ​​대해 몰랐습니다. 나는 상황이 여전히 매우 빠르게 변하고 있다고 생각한다. 이제 f64를 제네릭 유형으로 변경하려고하면 평생 문제가 발생합니다. 그러나 나는 그것에 대한 또 다른 질문을 쓸 것이다. –