[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 ...
随机推荐
- STM32的CRC32 测试代码
// STM32 CRC32 Test App - sourcer32@gmail.com #include <windows.h> #include <stdio.h> DW ...
- 使用cwRsync实现windows下文件定时同步
1.参考文献: 使用cwRsync实现windows下文件定时同步(备份) 文件同步工具CwRsync的使用方法及常用命令详解 2.背景: 当前的SCADA架构中,有1台Server,5台FE,还有1 ...
- [Go] 跨平台文件系统监控工具 fsnotify 应用举例
项目地址:https://github.com/fsnotify/fsnotify fsnotify 能监控指定文件夹内 文件的修改情况,如 文件的 增加.删除.修改.重命名等操作. 官方给出了以下注 ...
- 如何在socket编程的Tcp连接中实现心跳协议
from http://blog.csdn.net/nyist327/article/details/39586203 心跳包的发送,通常有两种技术方法1:应用层自己实现的心跳包 由应用程序自己发送心 ...
- Leetcode 234 Palindrome Linked List 复杂度为时间O(n) 和空间(1)解法
1. 问题描写叙述 给定一个单链表,推断其内容是不是回文类型. 比如1–>2–>3–>2–>1.时间和空间复杂都尽量低. 2. 方法与思路 1)比較朴素的算法. 因为给定的数据 ...
- VS2008 LINK : fatal error LNK1104: cannot open file 'atls.lib'错误解决方案
用VS 2008编写ATL的64位应用程序时,提示链接错误:VS2008 LINK : fatal error LNK1104: cannot open file 'atls.lib' 问题原因 VS ...
- ASP.NET MVC中使用Session来保持表单的状态
本篇实践在ASP.NET MVC 4下使用Session来保持表单的状态. 本篇的源码在这里: https://github.com/darrenji/KeepFormStateUsingSessio ...
- DirectInfo.GetFiles返回数组的默认排序
NTFS和CDFS下,是按照字母顺序,而FAT下,按照文件创建时间顺序 using System; using System.Collections; using System.IO; namespa ...
- Android控件之ImageSwticher
Android控件之ImageSwticher 1. ImageSwticher介绍 ImageSwitcher是图片切换的控件,它能实现图片切换时的动画效果,包括图片导入效果.图片消失效果等等.An ...
- 如何获得ios7系统中的蓝色
最简洁的办法,直接使用下列代码: [UIColor colorWithRed:0.0 green:122.0/255.0 blue:1.0 alpha:1.0] 最彻底的办法: OS7Colors是U ...