[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 ...
随机推荐
- [Go] Beego 模板嵌套 使用总结
通过以下文章,掌握了 Go 模板引擎 的基本用法: [Go] Template 使用简介 [Go] 模板嵌套最佳实践 Beego模板语法指南 但在开始学习 Beego 框架的 模板嵌套 模块源码时,有 ...
- Bootstrap碎语
这里记录下某段时间Bootstrap的零散碎片. 1.有关Bootstrap的参考网站: ● 官方:http://getbootstrap.com/● 主题:http://bootswatch.com ...
- chrome提示"安装unity web player"
网页浏览Unity3D制作的效果时,出现提示"安装unity web player".解决办法如下: →在浏览器中输入:chrome://flags/#enable-npapi 在 ...
- Java异常(三) 《Java Puzzles》中关于异常的几个谜题
概要 本章介绍<Java Puzzles>中关于异常的几个谜题.这一章都是以代码为例,相比上一章看起来更有意思.内容包括:谜题1: 优柔寡断谜题2: 极端不可思议谜题3: 不受欢迎的宾客谜 ...
- DELPHI新的变量的声明方法
DELPHI新的变量的声明方法 从DELPHI 10.3.1开始支持新的变量声明方法: procedure TForm1.查询1Click(Sender: TObject); begin var ur ...
- VisualStudio:添加现有项时使用添加为链接
这个特性很容易忘记使用(很多人可能还不知道),这里解释一下. 添加为链接是指:将指定的文件作为链接添加到项目中,这个文件在作用上和一般的文件没有区别,这样做的好处是可以多个项目共享一个文件,如:连接字 ...
- Weblogic跨域session冲突解决办法
一.现象: 在WebLogic中,有两个不同域A(端口:9000)和B(端口:8000),应用CA在域A中,应用CB在域B中,进行如下操作: 1.先登录应用CA,再登录应用CB,然后,切换回应用CA, ...
- 深入理解Java并发之synchronized实现原理
深入理解Java类型信息(Class对象)与反射机制 深入理解Java枚举类型(enum) 深入理解Java注解类型(@Annotation) 深入理解Java类加载器(ClassLoader) 深入 ...
- Eclipse中输入变量会自动补全上屏的解决方法
我自己在启动Eclipse代码补全后输入感觉确实爽多了,但是每次输入变量后一按下空格,编译器会自己帮你写一个很蛋疼的名字,比如你输入了:String mStr后按下空格,它就变成了mString,十分 ...
- POI Excel表格合并,边框设置
RegionUtil.setBorderLeft(1, cellRangeAddress, sheet, wb); RegionUtil.setBorderBottom(1, cellRangeAdd ...