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(, indx).concat(deck.slice(indx + ))
)
} const m = deck
.chain(drawCard())
.chain(drawCard())
.chain(drawCard())
.chain(drawCard()); 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())
.chain(drawCard())
.chain(drawCard())
.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(, indx).concat(deck.slice(indx + ))
)
}

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

const m = deck
.chain(drawCard())
.chain(drawCard())
.chain(drawCard())
.chain(drawCard()); 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(, indx).concat(deck.slice(indx + ))
)
} const m = deck
.chain(drawCard())
.chain(drawCard())
.chain(drawCard())
.chain(drawCard()); log(
look(m)
);

  

[Functional Programming] Using Last monoid with Maybe的更多相关文章

  1. Beginning Scala study note(4) Functional Programming in Scala

    1. Functional programming treats computation as the evaluation of mathematical and avoids state and ...

  2. Functional Programming without Lambda - Part 2 Lifting, Functor, Monad

    Lifting Now, let's review map from another perspective. map :: (T -> R) -> [T] -> [R] accep ...

  3. Functional Programming without Lambda - Part 1 Functional Composition

    Functions in Java Prior to the introduction of Lambda Expressions feature in version 8, Java had lon ...

  4. a primary example for Functional programming in javascript

    background In pursuit of a real-world application, let’s say we need an e-commerce web applicationfo ...

  5. Functional programming

    In computer science, functional programming is a programming paradigm, a style of building the struc ...

  6. Java 中的函数式编程(Functional Programming):Lambda 初识

    Java 8 发布带来的一个主要特性就是对函数式编程的支持. 而 Lambda 表达式就是一个新的并且很重要的一个概念. 它提供了一个简单并且很简洁的编码方式. 首先从几个简单的 Lambda 表达式 ...

  7. Functional programming idiom

    A functional programming function is like a mathematical function, which produces an output that typ ...

  8. 关于函数式编程(Functional Programming)

    初学函数式编程,相信很多程序员兄弟们对于这个名字熟悉又陌生.函数,对于程序员来说并不陌生,编程对于程序员来说也并不陌生,但是函数式编程语言(Functional Programming languag ...

  9. Functional Programming 资料收集

    书籍: Functional Programming for Java Developers SICP(Structure and Interpretation of Computer Program ...

随机推荐

  1. SQLSERVER误删Windows登录用户验证方式使用Windows身份验证的解决方法

    今天看到这篇文章:没有了SA密码,无法Windows集成身份登录,DBA怎么办? 想起来之前着急哥问我的一个问题,一个DBA删除了Windows登录用户,而且SQLSERVER服务器的验证方式是Win ...

  2. Revit API移动风管

    移动风管曲线就可以移动风管 , , ));//向上移动3         ts.Commit();         return Result.Succeeded;     } } url:http: ...

  3. WinForm MDIParent如何防止重复打开

    DI,全称是多文档界面(Multiple Document Interface),主要应用于基于图形用户界面的系统中.其目的是同时打开和显示多个文档,便于参考和编辑资料. 下面是一个WinForm M ...

  4. bat如何批量删除指定部分文件夹名的文件夹

    @echo offfor /f "delims=" %%i in ('dir /s/b/ad 123*') do ( rd /s/q "%%~i")exit

  5. 利用mvn deploy命令上传包(转)

    本文转自https://blog.csdn.net/chenaini119/article/details/52764543 mvn安装 下载maven的bin,在apache官方网站可以下载. ht ...

  6. WebRTC 基于GCC的拥塞控制(上)

    转自:http://blog.csdn.net/doitsjz/article/details/56481981 实时流媒体应用的最大特点是实时性,而延迟是实时性的最大敌人.从媒体收发端来讲,媒体数据 ...

  7. linux内核netfilter模块分析之:HOOKs点的注册及调用

    转自;http://blog.csdn.net/suiyuan19840208/article/details/19684883 -1: 为什么要写这个东西?最近在找工作,之前netfilter 这一 ...

  8. 明日传奇第三季/全集Legends of Tomorrow迅雷下载

    <明日传奇>第三季将加入一名新的女性角色.据Variety得到的消息称,塔拉·阿什(Tala Ashe)将作为<明日传奇>第三季的常规演员加入该剧.在第三季中,塔拉·阿什饰演的 ...

  9. 格林第一季/全集Grimm迅雷下载

    本季Grimm Season 1 (2011) 第一季看点:该剧由<天使调查团>的一对制片组合Jim Kouf.David Greenwalt打造. 该剧根据<格林童话>中的原 ...

  10. 权力的游戏第一季/全集Game of Thrones迅雷下载

    <权力的游戏>是一部中世纪史诗奇幻题材的电视连续剧,该剧以美国作家乔治·R·R·马丁的奇幻巨作<冰与火之歌>七部曲为基础改编创作. 故事背景中虚构的世界,分为两片大陆:位于西面 ...