[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(, 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的更多相关文章
- Beginning Scala study note(4) Functional Programming in Scala
1. Functional programming treats computation as the evaluation of mathematical and avoids state and ...
- Functional Programming without Lambda - Part 2 Lifting, Functor, Monad
Lifting Now, let's review map from another perspective. map :: (T -> R) -> [T] -> [R] accep ...
- 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 ...
- 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 ...
- Functional programming
In computer science, functional programming is a programming paradigm, a style of building the struc ...
- Java 中的函数式编程(Functional Programming):Lambda 初识
Java 8 发布带来的一个主要特性就是对函数式编程的支持. 而 Lambda 表达式就是一个新的并且很重要的一个概念. 它提供了一个简单并且很简洁的编码方式. 首先从几个简单的 Lambda 表达式 ...
- Functional programming idiom
A functional programming function is like a mathematical function, which produces an output that typ ...
- 关于函数式编程(Functional Programming)
初学函数式编程,相信很多程序员兄弟们对于这个名字熟悉又陌生.函数,对于程序员来说并不陌生,编程对于程序员来说也并不陌生,但是函数式编程语言(Functional Programming languag ...
- Functional Programming 资料收集
书籍: Functional Programming for Java Developers SICP(Structure and Interpretation of Computer Program ...
随机推荐
- HDU 4768 Flyer (2013长春网络赛1010题,二分)
Flyer Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submi ...
- sqlserver 2012 查询时提示“目录名称无效”
重装系统或者用360等软件清理了相应的临时文件导致解决:在运行中输入 %temp% 回车,会跳出找不到路径的提示,然后到提示的目录建没有找到的目录文件夹即可.
- AI 实验--v_JULY_v
http://blog.csdn.net/v_JULY_v http://www.julyedu.com/
- 使用Oracle DBLink进行数据库之间对象的訪问操作
Oracle中自带了DBLink功能,它的作用是将多个oracle数据库逻辑上看成一个数据库,也就是说在一个数据库中能够操作还有一个数据库中的对象,比如我们新建了一个数据database1.我们须要操 ...
- 基于ASP.NET WebAPI OWIN实现Self-Host项目实战
引用 寄宿ASP.NET Web API 不一定需要IIS 的支持,我们可以采用Self Host 的方式使用任意类型的应用程序(控制台.Windows Forms 应用.WPF 应用甚至是Windo ...
- Cocos2d-x3.0下实现循环列表
本文的实现是參照我之前在做iOS时实现的一个能够循环的列表这里用C++重写一遍. 效果: 原文地址:http://blog.csdn.net/qqmcy/article/details/2739301 ...
- MySQL对数据表进行分组查询
MySQL对数据表进行分组查询(GROUP BY) GROUP BY关键字可以将查询结果按照某个字段或多个字段进行分组.字段中值相等的为一组.基本的语法格式如下: GROUP BY 属性名 [HAVI ...
- CQRS:When to use CQRS
Now that we are at the end of our journey, we can suggest some of the criteria you should evaluate t ...
- [shiro学习笔记]第四节 使用源码生成Shiro的CHM格式的API文档
版本号为1.2.3的shiro API chm个事故文档生成. 获取shiro源码 编译生成API文档 转换成chm格式 API 获取shiro源码 shiro官网: http://shiro.apa ...
- Mac iterm2 创建服务器列表