• [Functional Programming] Introduction to State, thinking in State


    Recently, I am learning Working with ADT.

    Got some extra thought about State Monad. Basiclly how to thinking in State.

    First, we need to know the type of State: State returns Pair with Unit on the left, and state on the right:

    State(state => Pair(Unit, state))

    We don't need to manully add Pari and Unit type, State provides helper methods for doing this, but it is important to understand.

    On the left side, Unit is a variable, types can be very depends on your needs. 

    On the right side, state has to be a fixed type, you better do NOT change it.

    To access right side 'state', we can using 'modify' or 'set' helpers.

    To access left side 'Unit', we can using 'get' helper.

    One benifit by using State, is that we got lazyness for the function, means we can chain different state transition together.

    Let's see an example:

    const State = require('crocks/State');
    
    const {get, modify} = State;
    
    // add :: Int -> Int -> Int
    const add = x => y => y + x;
    // if we want to chain state transform, we need to have a function
    // addNickel :: () -> State Int ()
    //const addNickel = () => State(s => Pair(Unit(), s + 5))
    const addNickel = () => modify(add(5));
    // addDime = () -> State Int ()
    const addDime = () => modify(add(10));
    const addQuarter = () => modify(add(25));

    In the example, we define three 'addXX' functions, each add different values. 

    We can compose them together:

    // state :: State Int()
    const state = addNickel()
        .chain(addDime) // Pair( (), 15 )
        .chain(addQuarter) // Pair( (), 40 )
        .chain(addQuarter)  // Pair( (), 65 )
        .chain(addQuarter)  // Pair( (), 90 )
        .chain(addQuarter) // Pair( (), 115 )
    
    console.log(
        state
        .runWith(0)
    )

    It is important to call 'runWIth', 'execWith' or 'evalWith'... because State is lazy, you need to trigger it. We chain multi state together to get new state, or let's saying we are keep modfiying the state. At this point, we didn't touch the 'Unit' part.

    Then why 'Unit' / left side part can be useful?

    We can think 'Unit' / left side part is the result of 'state' / right side part after mapping to some logic / function.

    For example, we want to build a function, only return True of False, if number is greater than 100, return True, otherwise return False:

    // canVend :: Int -> Boolean
    const canVend = n => n >= 100;
    
    console.log(
        get()
       .map(canVend)
        .runWith(0)
    ) // False
    
    console.log(
        get()
       .map(canVend)
        .runWith(200)
    ) // True

    For calling 'get()', we are targeting left side part, which is 'Unit', it waiting some mapping function, which can transform state and put result into Unit. If we don't provide any mapping function, 'get()' will just copy the value from 'state':

    console.log(
        get()
        .runWith(10)
    ) // Pair(10, 10)
    
    console.log(
        get()
         .map(x => x * 2)
        .runWith(10)
    ) // Pair(20, 10)
    
    // the same as:
    console.log(
        get(x => x * 2)
        .runWith(10)
    ) // Pair(20, 10)

    In 'addNickle' example, we want to only get result in Boolean, if the state is greater than 100 or not, we can keep the state transform part untouched, only chain the getter logic in final state.

    // canVend :: Int -> Boolean
    const canVend = n => n >= 100;
    
    // evaluate :: () -> State Int Bool
    const evaluate = () => get(canVend); // get().map(fn) === get(fn)
    
    // state :: State Int()
    const state = addNickel()
        .chain(addDime) // Pair( (), 15 )
        .chain(addQuarter) // Pair( (), 40 )
        .chain(addQuarter)  // Pair( (), 65 )
        .chain(addQuarter)  // Pair( (), 90 )
        .chain(addQuarter) // Pair( (), 115 )
    
    console.log(
        state
        .chain(evaluate)// Pair( true, 115 )
        .runWith(0)
    )

    Full Code:

    ---

    const State = require('crocks/State');
    
    const {get, modify} = State;
    
    // add :: Int -> Int -> Int
    const add = x => y => y + x;// if we want to chain state transform, we need to have a function
    // addNickel :: () -> State Int ()
    //const addNickel = () => State(s => Pair(Unit(), s + 5))
    const addNickel = () => modify(add(5));
    // addDime = () -> State Int ()
    const addDime = () => modify(add(10));
    const addQuarter = () => modify(add(25));
    
    // canVend :: Int -> Boolean
    const canVend = n => n >= 100;
    
    // evaluate :: () -> State Int Bool
    const evaluate = () => get(canVend); // get().map(fn) === get(fn)
    
    // state :: State Int()
    const state = addNickel()
        .chain(addDime) // Pair( (), 15 )
        .chain(addQuarter) // Pair( (), 40 )
        .chain(addQuarter)  // Pair( (), 65 )
        .chain(addQuarter)  // Pair( (), 90 )
        .chain(addQuarter) // Pair( (), 115 )
    
    console.log(
        state
        .chain(evaluate)// Pair( true, 115 )
        .runWith(0)
    )
  • 相关阅读:
    秦曾昌人工智能课程---6、Decision Tree Learning
    秦曾昌人工智能课程---5、KNN和朴素贝叶斯
    秦曾昌人工智能课程---4、梯度下降算法
    我和最小二乘法的二三事
    英语影视台词---The Professor
    秦曾昌人工智能课程---3、机器学习中的哲学
    python从入门到大神---1、初始化实例、类属性、方法
    秒懂神经网络---震惊!!!神经网络原来可以这么简单!
    秦曾昌人工智能课程---2、机器学习中的数学基础2
    linuxc_螺纹锁紧pthread_mutex_t
  • 原文地址:https://www.cnblogs.com/Answer1215/p/10595663.html
Copyright © 2020-2023  润新知