[解決済み] Rustで関数を合成するには?
2023-06-22 20:20:56
質問
2つの関数を合成する関数を書こうとしています。最初のデザインはとてもシンプルです。2 つの関数を受け取り、合成された関数を返す関数で、Rust には残余パラメータがないので、他の関数と合成することができます。私は、役立つことのないコンパイラーエラーに苛立ちながら、壁にぶつかっています。
私のコンポジット関数です。
fn compose<'a, A, B, C, G, F>(f: F, g: G) -> Box<Fn(A) -> C + 'a>
where
F: 'a + Fn(A) -> B + Sized,
G: 'a + Fn(B) -> C + Sized,
{
Box::new(move |x| g(f(x)))
}
どのように使いたいのか
fn main() {
let addAndMultiply = compose(|x| x * 2, |x| x + 2);
let divideAndSubtract = compose(|x| x / 2, |x| x - 2);
let finally = compose(*addAndMultiply, *divideAndSubtract);
println!("Result is {}", finally(10));
}
コンパイラはそれが気に入らないようで、何をやってもtrait boundsが満たされない。というエラーが出ます。
error[E0277]: the size for values of type `dyn std::ops::Fn(_) -> _` cannot be known at compilation time
--> src/main.rs:13:19
|
13 | let finally = compose(*addAndMultiply, *divideAndSubtract);
| ^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `dyn std::ops::Fn(_) -> _`
= note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
note: required by `compose`
--> src/main.rs:1:1
|
1 | / fn compose<'a, A, B, C, G, F>(f: F, g: G) -> Box<Fn(A) -> C + 'a>
2 | | where
3 | | F: 'a + Fn(A) -> B + Sized,
4 | | G: 'a + Fn(B) -> C + Sized,
5 | | {
6 | | Box::new(move |x| g(f(x)))
7 | | }
| |_^
どのように解決するのですか?
として @ljedrz が指摘するように のように、構成された関数を再度参照するだけで、動作するようになります。
let finally = compose(&*multiply_and_add, &*divide_and_subtract);
(Rustでは、慣習として変数名はsnake_caseであるべきとされていることに注意してください)
しかし、我々はこれを改善することができます!
Rust 1.26以降では、このように
抽象的な戻り値の型
を使うことができるようになりました (以前は
#![feature(conservative_impl_trait)]
). これは、ライフタイム、リファレンスを省略することができるので、例を非常に単純化することができます。
Sized
制約、そして
Box
があります。
fn compose<A, B, C, G, F>(f: F, g: G) -> impl Fn(A) -> C
where
F: Fn(A) -> B,
G: Fn(B) -> C,
{
move |x| g(f(x))
}
fn main() {
let multiply_and_add = compose(|x| x * 2, |x| x + 2);
let divide_and_subtract = compose(|x| x / 2, |x| x - 2);
let finally = compose(multiply_and_add, divide_and_subtract);
println!("Result is {}", finally(10));
}
最後に、あなたはレストパラメータについて言及しているので、あなたが実際に欲しいのは、柔軟な方法で好きなだけ関数を連鎖的に構成する方法なのではないかと思います。私はこの目的のためにこのマクロを書きました。
macro_rules! compose {
( $last:expr ) => { $last };
( $head:expr, $($tail:expr), +) => {
compose_two($head, compose!($($tail),+))
};
}
fn compose_two<A, B, C, G, F>(f: F, g: G) -> impl Fn(A) -> C
where
F: Fn(A) -> B,
G: Fn(B) -> C,
{
move |x| g(f(x))
}
fn main() {
let add = |x| x + 2;
let multiply = |x| x * 2;
let divide = |x| x / 2;
let intermediate = compose!(add, multiply, divide);
let subtract = |x| x - 2;
let finally = compose!(intermediate, subtract);
println!("Result is {}", finally(10));
}
関連
-
[解決済み] 関数型プログラミングで時間関数が存在するのはなぜですか?
-
[解決済み] クロージャ」とは何ですか?
-
[解決済み] Rustのユニットテストでprintln! が動作しないのはなぜ?
-
[解決済み】関数型プログラミングで、ファンクターとは何ですか?
-
[解決済み】手続き型プログラミングと関数型プログラミングの違いは何ですか?[クローズド]
-
[解決済み] ステートレス・プログラミングのメリット?
-
[解決済み] 純粋関数型言語において、逆関数を得るためのアルゴリズムはあるか?
-
[解決済み] 中型のClojureサンプルアプリケーション?
-
[解決済み] 関数型プログラミング言語はいつ使う?[クローズド]
-
[解決済み] Schemeのeq, eqv, equal, =の違いは何ですか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] ネストされた関数。副作用の不適切な使用?
-
[解決済み] (関数型)リアクティブプログラミングとは?
-
[解決済み] Y-combinatorとは?[クローズド]
-
[解決済み】ミュータブルステートなしで何か役に立つことができるのか?
-
[解決済み】関数型プログラミングで、ファンクターとは何ですか?
-
[解決済み】なぜ関数型プログラミングはまだ浸透していないのでしょうか?
-
[解決済み] なぜLispを学ばなければならないのか?[クローズド]
-
[解決済み] Curry-Howard Isomorphismから生じる最も興味深い同値性とは?
-
[解決済み] 中型のClojureサンプルアプリケーション?
-
[解決済み] 社会人のためのコンビナート説明会