• [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)
    );

      

  • 相关阅读:
    数据库常用的锁有哪些
    如何在vue3.0 vue-cli 3.x中使用jquery
    带你体验Vue2和Vue3开发组件有什么区别
    snf帆软FineReport安装,布署,配置-王春天
    SNF开发平台WinForm之--审核流使用方式
    Linux上安装服务器监视工具,名为pyDash。
    Linux上安装服务器监视工具,名为Scout_Realtime。
    bootstrap fileinput(帮助文档URL)
    【转】Windows下使用Graalvm将Javafx应用编译成exe
    【转】Windows下使用Graalvm将Javafx应用编译成exe
  • 原文地址:https://www.cnblogs.com/Answer1215/p/10609818.html
Copyright © 2020-2023  润新知