Rustにおける関数型プログラミング


ヶ月前に私が最初に非営利プロジェクトを開発したときReasonML , 私は新しい経験を得た、機能的プログラミングです.この言語はOCaml . OCaml自体は純粋に機能的な言語であり、提供される機能は興味深いです.例えば:型推論、強型システム、代数的データ型、および多く.面白い右?
それを試みた後、私は関数型プログラミングに興味を持ち始めました.最後に、関数パラダイムを異なる言語で実装しようとしましたRust .

導入
機能的プログラミング(FP)は、我々が表現的で簡潔で、エレガントなコードを書くことができるプログラミングパラダイムです.機能的プログラミングは、開発者がそれが何度も何度も同じコードを書いていないことを意味しています(それを自分自身を繰り返すことはありません)乾燥しないようにコードを管理する開発者を支援します.Lisp、クロジュール、アーラン、Haskell、R、および多くのような例のための他の機能言語.

OKしかし、なぜ錆?
問題はさび関数言語ですか?Rust自身は言語のMLの家族に触発されたが、それは機能していない錆.しかし幸いにも、Rustは他の機能言語に類似しているいくつかの特徴を持っています.

目次
  • Primitive Types
  • Closures
  • Currying
  • Higher Order Functions(HOF)
  • Lazy evaluations

  • Primitive Types

    In order not to jump right away, it would be nice if we had to know several data types in Rust. This also applies to all programming languages.


    ブールリアン
    最も基本的なデータ型は単純ですtrue / false 価値は、それが呼ばれるさびてbool
    let x = true;
    let y: bool = false;
    

    チャー
    The char データ型には単一のUnicode値があります.私たちはchar 単一のtickによるデータ型' )
    let x = 'x';
    let two_hearts = '💕';
    
    他の言語とは異なり、char 錆は1バイトではなく、4バイトです.

    数値型
    Rustは符号付きのようないくつかの数値型カテゴリバリアントを持ちますi ) 符号なしu ), 固定サイズ8 , 16 , 32 , 64 ) と変数isize , usize ) 種類
    let x = 42; // `x` has type `i32`.
    let y = 1.0; // `y` has type `f64`.
    

    アレイ
    他の多くのプログラミング言語と同様に、Rustには配列データ型もあります.デフォルトでは、rustの配列を変更できません.を初期化しない限り mut
    let a = [1, 2, 3]; // a: [i32; 3]
    let mut m = [1, 2, 3]; // m: [i32; 3]
    

    関数
    また、データ型があります!次のようにします.
    fn foo(x: i32) -> i32 { x }
    let x: fn(i32) -> i32 = foo;
    
    この場合、foo () 関数は戻り型numeric: i32 , 返り値x .

    For more information, you can check here: primitive types



    Closures

    Closure is a mechanism by which an inner function will have access to the variables defined in its outer function’s lexical scope even after the outer function has returned. Up to here understand? in short closures is an inner function that has access to retrieve a value throughout the scope both inside and outside.

    fn fmt(prev_str: &str) -> String {
        let mut new_str = String::new();
    
        let closure_annotated = |next_str| -> String {
            new_str.push_str(prev_str);
            new_str.push_str(next_str);
            return new_str;
        };
    
        closure_annotated("dolor sit amet")
    }
    
    let r_txt = "Lorem ipsum ";
    assert_eq!("Lorem ipsum dolor sit amet", fmt(r_txt));
    

    In this case, in new_str.push_str () section where closure_annotated accesses the new_str variable then changes the value.


    Currying

    Currying is a process in functional programming in which we can transform a function with multiple arguments into a sequence of nesting functions. It returns a new function that expects the next argument inline.

    #[derive(Debug)]
    struct States<'a> {
        a: &'a i32,
        b: &'a i32,
    }
    
    trait Currying {
        type ReturnType: Fn(i32) -> i32;
        fn add(self) -> Self::ReturnType;
    }
    
    impl Currying for States<'static>{
        type ReturnType = Box<dyn Fn(i32) -> i32>;
    
        fn add(self) -> Self::ReturnType {
            Box::new(move|x| {
                x * self.a
            })
        }
    }
    
    let r_value: States = States {
        a: &100,
        b: &100
    };
    
    let r1 = r_value.add();
    let r2 = r1(5);
    
    assert_eq!(500, r2);
    
    
    There are 2 parameters here, namely a , b where each has a numeric data type, then in trait section is a function interface, a place for initializing functions. These traits are similar to typescript interfaces .

    Higher Order Functions(HOF)

    Higher order functions are functions that use other functions as parameters or as a result of returns.

    fn map<F>(arr: &[i32], func: F) -> Vec<i32> where F: Fn(&i32) -> i32{
        let mut new_array: Vec<i32> = vec![];
        for i in arr.iter() {
            new_array.push(func(i))
        }
    
        return new_array
    }
    
    let lists = vec![1, 4, 9, 16];
    let result = map(&lists, |i| *i + 2);
    
    assert_eq!(vec![3, 6, 11, 18], result)
    

    So func and map are higher order functions, where this function is used to change every contents of an array. The return result is a new array of the same length as the modified originalArray .


    Lazy Evaluation

    Lazy evaluation or non-strict evaluation is a process of holding the evaluation of an expression/function until the value is needed. The goal is to avoid repeated evaluations.

    struct State {
        x: i32,
    }
    
    trait Lazy {
        fn add(&self) -> i32;
        fn multiply(&self) -> i32;
        fn add_or_multiply(&self, add: bool) -> i32;
    }
    
    impl Lazy for State {
        fn add(&self) -> i32 {
            println!("executing add");
            &self.x + &self.x
        }
    
        fn multiply(&self) -> i32 {
            println!("executing multiply");
            &self.x * &self.x
        }
    
        fn add_or_multiply(&self, add: bool) -> i32 { 
            match add {
                true => self.add(),
                false =>  self.multiply(),
            }
        }
    }
    
    let val: State = State {
        x: 20
    };
    
    assert_eq!(40, val.add_or_multiply(true));
    assert_eq!(400, val.add_or_multiply(false));
    

    Functional programming (FP) provides many advantages, and its popularity has been increasing as a result. However, each programming paradigm comes with its own unique jargon and FP is no exception. By providing a glossary, i hope to make learning FP easier✌️

    Source code: rustfp.github