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. 采用ASP.NET IIS 注册工具 (Aspnet_regiis.exe)对web.config实行本地加密

    加密原因:我们通常将一些重要的配置信息写在Web.config里面,其中数据库链接就是这样的信息.将这些数据直接明文显示,显然不太安全. 工具: 采用ASP.NET IIS 注册工具 (Aspnet_ ...

  2. Go 面试题(附答案解析)

    1.写出下面代码输出内容 package main import ( "fmt" ) func main() { defer_call() } func defer_call() ...

  3. 分享下使用 svn,测试服务器代码自动更新、线上服务器代码手动更新的配置经验

    分享下使用 svn,测试服务器代码自动更新.线上服务器代码手动更新的配置经验 利用SVN的POST-COMMIT钩子自动部署代码 Linux SVN 命令详解 Linux SVN 命令详解2 使用sv ...

  4. mysql 阿里内核人员

    丁奇 http://dinglin.javaeye.com/ 鸣嵩 @曹伟-鸣嵩 (新浪微博) 彭立勋 http://www.penglixun.com/ 皓庭 http://wqtn22.iteye ...

  5. Win10系统下如何禁止同步主机session?windows 10禁止同步主机session的方法

    近来,有些刚刚升级Win10正式版的用户反映自己的电脑开机时有个同步主机session启动项占用了将近半分钟,而选择用360禁止后,下次会出现同步主机session3,再禁止下次又会出现同步主机ses ...

  6. 娓娓道来c指针 (7)指针运算

    (7)指针运算 在前几篇文章中,我们已经见过指针运算的使用场景,并多次使用指针运算来进行验证. 这里我们来特别地总结下.指针运算的本质含义. 在c语言中.如果p.pa.pb都是某种类型的指针,这种运算 ...

  7. Unity3D实践系列07,组件的启用或禁用开关,物体的的可见或不可见开关,以及相应事件

    创建一个Unity项目. 在"Project"窗口中,在"Asserts"中,添加"_MyScene"文件夹. 点击"File&q ...

  8. DXT 图片压缩(DXTC/DirectX Texture Compression Overview)

    这两天在写 DDS 格式的解码程序.DDS 是微软为 DirectX 开发的一种图片格式,MSDN 上可以查到其文件格式说明: http://msdn2.microsoft.com/en-us/lib ...

  9. RabbitMQ的应用场景以及基本原理介绍(转)

    本文转自https://blog.csdn.net/whoamiyang/article/details/54954780 1.背景 RabbitMQ是一个由erlang开发的AMQP(Advanve ...

  10. sql 注释 语法

    mysql 服务器支持 # 到该行结束.-- 到该行结束 以及 /* 行中间或多个行 */ 的注释方格: mysql> SELECT 1+1; # 这个注释直到该行结束mysql> SEL ...