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

      

  • 相关阅读:
    信号量的实现
    锁的实现
    Do PDB Files Affect Performance?
    分布式系统一致性算法(Raft)
    idea开发工具破解地址
    IOS设备上传图片,使用ImageIO.write 图片翻转纠正(JAVA)
    使用WebSocket进行消息推送(附代码)
    SpringCloud构建微服务 | 服务注册与发现(一)提供Demo
    @slf4j注解找不到log变量-已解决
    springBoot事务无法回滚 MyISAM与InnoDB区别
  • 原文地址:https://www.cnblogs.com/Answer1215/p/10609818.html
Copyright © 2011-2022 走看看