zoukankan      html  css  js  c++  java
  • [Functional Programming] Using Last monoid with Maybe

    Imaging we have a deck of cards, eveytimes we need to pick one card from deck, the result we want to have is:

    // Selected: "A♠", 
    // Remaining: [ "2♠", "3♠", "4♠", "5♠", "6♠", "7♠", "8♠", "9♠", "10♠", "J♠", "Q♠", "K♠", "A♣", "2♣", "3♣", "4♣", "5♣", "6♣", "7♣", "8♣", "9♣", "10♣", "J♣", "Q♣", "K♣", "A♥", "2♥", "3♥", "4♥", "5♥", "6♥", "7♥", "8♥", "9♥", "10♥", "J♥", "Q♥", "K♥", "A♦", "2♦", "3♦", "4♦", "5♦", "6♦", "7♦", "8♦", "9♦", "10♦", "J♦", "Q♦", "K♦" ] '

    For selected, each time we want only one! remaining should be all the rest of cards which have been choosen. If we draw more than one time, remining cards should be reduced.

    For *Selected + remaining* pair, we can consider to use 'Pair' monad from ADT. Then our results looks like:

    // 'Pair( "A♠", [ "2♠", "3♠", "4♠", "5♠", "6♠", "7♠", "8♠", "9♠", "10♠", "J♠", "Q♠", "K♠", "A♣", "2♣", "3♣", "4♣", "5♣", "6♣", "7♣", "8♣", "9♣", "10♣", "J♣", "Q♣", "K♣", "A♥", "2♥", "3♥", "4♥", "5♥", "6♥", "7♥", "8♥", "9♥", "10♥", "J♥", "Q♥", "K♥", "A♦", "2♦", "3♦", "4♦", "5♦", "6♦", "7♦", "8♦", "9♦", "10♦", "J♦", "Q♦", "K♦" ] )'

    If chain drawCard a scond time, the results should be:

    // 'Pair( "2♠", [ "3♠", "4♠", "5♠", "6♠", "7♠", "8♠", "9♠", "10♠", "J♠", "Q♠", "K♠", "A♣", "2♣", "3♣", "4♣", "5♣", "6♣", "7♣", "8♣", "9♣", "10♣", "J♣", "Q♣", "K♣", "A♥", "2♥", "3♥", "4♥", "5♥", "6♥", "7♥", "8♥", "9♥", "10♥", "J♥", "Q♥", "K♥", "A♦", "2♦", "3♦", "4♦", "5♦", "6♦", "7♦", "8♦", "9♦", "10♦", "J♦", "Q♦", "K♦" ] )'

    Now we can pretty much know the Pair should be:

    // Deck :: Pair (Last Card) [Card]

    We use Last and Array tow semigourps, so the value know how to concat themselves, 'Last' will just keep the last value, Array will concat each other.

    We have deck.js file which provides data:

    const Last = require('crocks/Last');
    const Pair = require('crocks/Pair');
    
    const assign = require('crocks/helpers/assign');
    const chain = require('crocks/pointfree/chain');
    const liftA2 = require('crocks/helpers/liftA2');
    const map = require('crocks/pointfree/map');
    const reduce = require('crocks/pointfree/reduce')
    
    const suits = [
        { suit: '♠', color: 'dark' },
        { suit: '♣', color: 'dark' },
        { suit: '♥', color: 'light' },
        { suit: '♦', color: 'light' },
      ]
    
    const values = [
        { value: 1, face: 'A' },
        { value: 2, face: '2' },
        { value: 3, face: '3' },
        { value: 4, face: '4' },
        { value: 5, face: '5' },
        { value: 6, face: '6' },
        { value: 7, face: '7' },
        { value: 8, face: '8' },
        { value: 9, face: '9' },
        { value: 10, face: '10' },
        { value: 11, face: 'J' },
        { value: 12, face: 'Q' },
        { value: 13, face: 'K' },
      ];
    
    // Deck :: Pair (Last Card) [Card]  
    // deck :: Deck
    const deck = Pair(Last.empty(), liftA2(assign, suits, values));
    // displayCard :: Card -> String
    const displayCard = ({face, suit}) =>
      `${face}${suit}`;
    // displayCards :: [Card] -> [String]
    const displayCards = map(displayCard);
    
    // pickCard : [ Card ] -> Pair [Card][Card]
    const pickCard = cs => {
      const idx = Math.floor(Math.random() * cs.length);
    
      return Pair(
          [].concat(cs[idx]),
          cs.slice(0, idx).concat(cs.slice(idx + 1))
      )
    }
    // shuffleCards : [ Cards ] -> [ Cards ]
    const shuffleCards = cards => reduce(
      chain(pickCard), Pair([], cards), cards
    ).fst();
    
    module.exports = {
        deck,
        displayCard,
        displayCards,
        pickCard,
        shuffleCards
    }
    

    Important here, is to know how we define our 'deck' to Pair monad with Last and Array.

    // Deck :: Pair (Last Card) [Card]  
    // deck :: Deck
    const deck = Pair(Last.empty(), liftA2(assign, suits, values));

    For our consumer part, we can do:

    const log = require('./lib/log');
    const Last = require('crocks/Last');
    const Pair = require('crocks/Pair');
    
    const bimap = require('crocks/pointfree/bimap');
    
    const {deck, displayCard, displayCards} = require('./model/deck');
    
    const look = bimap(
        x => displayCard(x.option('')), 
        displayCards
    );
    
    // Deck :: Pair (Last Card) [Card]
    // drawCard : Int -> [Card] -> Deck
    const drawCard = indx => deck => {
        return Pair(
            Last(deck[indx]),
            deck.slice(0, indx).concat(deck.slice(indx + 1))
        )
    }
    
    const m = deck
        .chain(drawCard(0))
        .chain(drawCard(0))
        .chain(drawCard(0))
        .chain(drawCard(0));
    
    log(
        look(m)
    );
    // 'Pair( "4♠", [ "5♠", "6♠", "7♠", "8♠", "9♠", "10♠", "J♠", "Q♠", "K♠", "A♣", "2♣", "3♣", "4♣", "5♣", "6♣", "7♣", "8♣", "9♣", "10♣", "J♣", "Q♣", "K♣", "A♥", "2♥", "3♥", "4♥", "5♥", "6♥", "7♥", "8♥", "9♥", "10♥", "J♥", "Q♥", "K♥", "A♦", "2♦", "3♦", "4♦", "5♦", "6♦", "7♦", "8♦", "9♦", "10♦", "J♦", "Q♦", "K♦" ] )'

    Thre is one problem: 

    if we change last chain call to:

    const m = deck
        .chain(drawCard(0))
        .chain(drawCard(0))
        .chain(drawCard(0))
        .chain(drawCard(99));

    Our code throw error:

    TypeError: Cannot match against 'undefined' or 'null'.

    This is because, index 99 is out or range;

    Last(deck[indx]),

    Because Last can take a single value or a Maybe type, and return when value is present and wrap with Just, or return Nothing, which means we can prevent this error happens by add Maybe:

    const safe = require('crocks/Maybe/safe');
    const isDefined = require('crocks/predicates/isDefined');
    
    // isValid :: a -> Maybe a
    const isValid = safe(isDefined);
    
    // Deck :: Pair (Last Card) [Card]
    // drawCard : Int -> [Card] -> Deck
    const drawCard = indx => deck => {
        return Pair(
            // Last can take Maybe or value in arguement, 
            // make it possible to control the code safety
            Last(isValid(deck[indx])),
            deck.slice(0, indx).concat(deck.slice(indx + 1))
        )
    }

    Now if we still try to get 99 index, it will just ignore it:

    const m = deck
        .chain(drawCard(0))
        .chain(drawCard(0))
        .chain(drawCard(0))
        .chain(drawCard(99));
    
    log(
        look(m)
    );
    //'Pair( "3♠", [ "4♠", "5♠", "6♠", "7♠", "8♠", "9♠", "10♠", "J♠", "Q♠", "K♠", "A♣", "2♣", "3♣", "4♣", "5♣", "6♣", "7♣", "8♣", "9♣", "10♣", "J♣", "Q♣", "K♣", "A♥", "2♥", "3♥", "4♥", "5♥", "6♥", "7♥", "8♥", "9♥", "10♥", "J♥", "Q♥", "K♥", "A♦", "2♦", "3♦", "4♦", "5♦", "6♦", "7♦", "8♦", "9♦", "10♦", "J♦", "Q♦", "K♦" ] )'

    ---

    Full code index.jks:

    const log = require('./lib/log');
    const Last = require('crocks/Last');
    const Pair = require('crocks/Pair');
    
    const bimap = require('crocks/pointfree/bimap');
    const safe = require('crocks/Maybe/safe');
    const isDefined = require('crocks/predicates/isDefined');
    
    const {deck, displayCard, displayCards} = require('./model/deck');
    
    const look = bimap(
        x => displayCard(x.option('')), 
        displayCards
    );
    
    // isValid :: a -> Maybe a
    const isValid = safe(isDefined);
    
    // Deck :: Pair (Last Card) [Card]
    // drawCard : Int -> [Card] -> Deck
    const drawCard = indx => deck => {
        return Pair(
            // Last can take Maybe or value in arguement, 
            // make it possible to control the code safety
            Last(isValid(deck[indx])),
            deck.slice(0, indx).concat(deck.slice(indx + 1))
        )
    }
    
    const m = deck
        .chain(drawCard(0))
        .chain(drawCard(0))
        .chain(drawCard(0))
        .chain(drawCard(99));
    
    log(
        look(m)
    );

      

  • 相关阅读:
    linux查看端口号监听状态
    linux / centos 安装SQL Server 2017 设置默认语言与排序规则Chinese_PRC_CI_AS
    centos 生产环境部署 asp.net core
    shell参数处理模板
    搜狗语料库数据整编
    Call From master/192.168.128.135 to master:8485 failed on connection exception: java.net.ConnectException: Connection refused
    spark-shell启动报错:Yarn application has already ended! It might have been killed or unable to launch application master
    webmagic爬取博客园所有文章
    jdbc链接hive报错:java.lang.ClassNotFoundException: org.apache.thrift.transport.TTransport
    NotePad++ 正则表达式替换 高级用法 [转]
  • 原文地址:https://www.cnblogs.com/Answer1215/p/10609818.html
Copyright © 2011-2022 走看看