Principles of Clean Functional Programming
Karl Bielefeldt
@softwarewhys
// http://stackoverflow.com/q/36413148/389146
var typeName: String = ""
if (stringTypes.contains(label)) {
typeName = "string"
} else if (floatingTypes.contains(label)) {
typeName = "float"
} else if (encodingTypes.contains(label)) {
typeName = "encoding"
} else if (rangeTypes.contains(label)) {
typeName = "range"
}
val types = Map(stringTypes -> "string",
floatingTypes -> "float",
encodingTypes -> "encoding",
rangeTypes -> "range")
types find (_._1 contains label) map (_._2)
getOrElse "label not found"
// http://softwareengineering.stackexchange.com/a/342038/3965
const obj1 = { a:1, b:2, c:3, d:3 };
const obj2 = { a:1, b:1, e:2, f:2, g:3, h:5 };
const getXs = (obj, getX) =>
Object.keys(obj).map(key => getX(obj)(key));
const getPctSameXs = (getX, filter = vals => vals) =>
(objA, objB) =>
filter(getXs(objB, getX))
.reduce(
(numSame, x) =>
getXs(objA, getX).indexOf(x) > -1 ? numSame + 1 :
numSame, 0
) / Object.keys(objA).length * 100;
const pctSameKeys =
getPctSameXs(obj => key => key);
const pctSameValsDups =
getPctSameXs(obj => key => obj[key]);
const pctSameValsNoDups =
getPctSameXs(obj => key => obj[key],
vals => [...new Set(vals)]);
const pctSameProps = getPctSameXs(obj => key =>
JSON.stringify( {[key]: obj[key]} ));
function mapObject(obj, f) {
return Object.keys(obj).map(key => f(obj, key));
}
function getPercentSame(obj1, obj2, f) {
const mapped1 = mapObject(obj1, f);
const mapped2 = mapObject(obj2, f);
const same = mapped1.filter(x => mapped2.indexOf(x) != -1);
const percent = same.length / mapped1.length * 100;
return percent;
}
const getValues = (obj, key) => obj[key];
const valuesWithDupsPercent =
getPercentSame(obj1, obj2, getValues);
Pure functions are easier to:
Reorder
Refactor
Parallelize
Typecheck
Share data between
Test
Compose
val conversions = List(
(1000, "M"),
(900, "CM"),
(500, "D"),
(400, "CD"),
(100, "C"),
(90, "XC"),
(50, "L"),
(40, "XL"),
(10, "X"),
(9, "IX"),
(5, "V"),
(4, "IV"),
(1, "I"))
def intToRoman(number: Int): Unit = {
if (number == 0) {
println()
} else {
val (decimal, roman) =
(conversions dropWhile (_._1 > number)).head
print(roman)
intToRoman(number - decimal)
}
}
def intToRoman(number: Int): String = {
if (number == 0) {
""
} else {
val (decimal, roman) =
(conversions dropWhile (_._1 > number)).head
roman + intToRoman(number - decimal)
}
}
def printRoman = intToRoman _ andThen println
// http://stackoverflow.com/a/15195230/389146
def foo(arguments: Seq[(String, String)],
merges: Seq[(String, String)]) =
{
val metadata: Seq[Attribute] = (arguments ++ merges)
.groupBy(_._1)
.map {
case (key, value) =>
Attribute(None, key,
Text(value.map(_._2).mkString(" ") ), Null)
}
var result: MetaData = Null
for(m <- metadata) result = result.append( m )
result
}
def foo(arguments: Seq[(String, String)],
merges: Seq[(String, String)]) =
(arguments ++ merges).groupBy(_._1).map {
case (key, value) => Attribute(None, key,
Text(value.map(_._2).mkString(" ")), Null)
}.fold(Null)((soFar, attr) => soFar append attr)
def foo(arguments: Seq[(String, String)], merges: Seq[(String, String)]) =
(arguments ++ merges).groupBy(_._1).map {
case (key, value) => Attribute(None, key, Text(value.map(_._2).mkString(" ")), Null)
}.fold(Null)((soFar, attr) => soFar append attr)
type PairSeq = Seq[(String, String)]
def combineText(text: PairSeq): Text =
Text(text.map(_._2).mkString(" "))
def foo(arguments: PairSeq, merges: PairSeq) = {
val metadata = (arguments ++ merges)
.groupBy(_._1)
.map{case (key, value) =>
Attribute(None, key, combineText(value), Null)
}
metadata.fold(Null)((xs, attr) => xs append attr)
}
def intToRoman(number: Int): String = {
if (number == 0) {
""
} else {
val (decimal, roman) = number match {
case x if x > 1000 => (1000, "M")
case x if x > 900 => (900, "CM")
case x if x > 500 => (500, "D")
case x if x > 400 => (400, "CD")
case x if x > 100 => (100, "C")
case x if x > 90 => (90, "XC")
case x if x > 50 => (50, "L")
case x if x > 40 => (40, "XL")
case x if x > 10 => (10, "X")
case x if x > 9 => (9, "IX")
case x if x > 5 => (5, "V")
case x if x > 4 => (4, "IV")
case _ => (1, "I")
}
roman + intToRoman(number - decimal)
}
}
val conversions = List(
(1000, "M"),
(900, "CM"),
(500, "D"),
(400, "CD"),
(100, "C"),
(90, "XC"),
(50, "L"),
(40, "XL"),
(10, "X"),
(9, "IX"),
(5, "V"),
(4, "IV"),
(1, "I"))
def intToRoman(number: Int): String = {
if (number == 0) {
""
} else {
val (decimal, roman) =
(conversions dropWhile (_._1 > number)).head
roman + intToRoman(number - decimal)
}
}
const obj1 = { a:1, b:2, c:3, d:3 };
const obj2 = { a:1, b:1, e:2, f:2, g:3, h:5 };
const getXs = (obj, getX) =>
Object.keys(obj).map(key => getX(obj)(key));
const getPctSameXs = (getX, filter = vals => vals) =>
(objA, objB) =>
filter(getXs(objB, getX))
.reduce(
(numSame, x) =>
getXs(objA, getX).indexOf(x) > -1 ? numSame + 1 :
numSame, 0
) / Object.keys(objA).length * 100;
const pctSameKeys =
getPctSameXs(obj => key => key);
const pctSameValsDups =
getPctSameXs(obj => key => obj[key]);
const pctSameValsNoDups =
getPctSameXs(obj => key => obj[key],
vals => [...new Set(vals)]);
const pctSameProps = getPctSameXs(obj => key =>
JSON.stringify( {[key]: obj[key]} ));
function mapObject(obj, f) {
return Object.keys(obj).map(key => f(obj, key));
}
function getPercentSame(obj1, obj2, f) {
const mapped1 = mapObject(obj1, f);
const mapped2 = mapObject(obj2, f);
const same = mapped1.filter(x => mapped2.indexOf(x) != -1);
const percent = same.length / mapped1.length * 100;
return percent;
}
const getValues = (obj, key) => obj[key];
const valuesWithDupsPercent =
getPercentSame(obj1, obj2, getValues);
val in: Option[String] = ???
val out: Option[Int] = in match {
case Some(x) => Try(x.toInt).toOption
case None => None
}
Try(x.toInt).toOption}
def intToRoman(number: Int): String = {
def stream = Stream.iterate((number, "")){
case (decimal, roman) => val (decDigit, romDigit) =
(conversions dropWhile (_._1 > decimal)).head
(decimal - decDigit, roman + romDigit)
}
stream.dropWhile(_._1 > 0).map(_._2).head
}
def sumToN(n: Int): Int = {
if (n == 0)
0
else
n + sumToN(n-1)
}
@scala.annotation.tailrec
def sumToN(n: Int, acc: Int = 0): Int = {
if (n == 0)
acc
else
sumToN(n-1, acc+n)
}
def sumToN(n: Int): Int = (0 to n).sum
def sumToN(n: Int): Int = 0 to n reduce (_ + _)
// http://stackoverflow.com/q/42306911/389146
def pair(a: Array[Int], target: Int): Option[(Int, Int)] = {
var l = 0
var r = a.length - 1
var result: Option[(Int, Int)] = None
while (l < r && result.isEmpty) {
(a(l), a(r)) match {
case (x, y) if x + y == target => result = Some(x, y)
case (x, y) if x + y < target => l = l + 1
case (x, y) if x + y > target => r = r - 1
}
}
result
}
def findSum(a: Vector[Int],
target: Int): Option[(Int, Int)] = {
def stream = Stream.iterate(a){xs =>
if (xs.head + xs.last > target)
xs.init
else
xs.tail
}
stream.take (a.size - 1)
.map {xs => (xs.head, xs.last)}
.find {case (x,y) => x + y == target}
}
// http://stackoverflow.com/a/35051921/389146
def getOpt(p: (Int) => Boolean): Option[Tree[Int]] = {
def _getOpt(tree: Tree[Int],
p: (Int) => Boolean): Option[Tree[Int]] = {
tree.children.map {t =>
if(p(t.node))
Some(t)
else
t.children.map(_getOpt(_, p))
}
}
}
def depthFirstTraverse[A](tree: Tree[A]): Stream[Tree[A]] =
tree #:: (tree.children map depthFirstTraverse)
.fold(Stream.Empty)(_ ++ _)
def getOpt(p: (Int) => Boolean): Option[Tree[Int]] =
depthFirstTraverse(tree) find {x => p(x.node)}
case class Room(description: String,
monsters: List[Monster],
loot: List[Loot],
north: Option[Room],
south: Option[Room],
east: Option[Room],
west: Option[Room])
case class Room(description: String,
monsters: List[Monster],
loot: List[Loot],
doors: List[Direction])
val rooms = Map((0,0) -> Room("scary room",
List(scaryMonster),
List(coolLoot),
List(North)),
(0,1) -> Room("fun room",
List(clown),
List(partyFavor),
List(South,East)))
case class Board(forward: Map[String, Node],
reverse: Map[Node, String],
occupiedSquares: List[String],
nodes: List[Node])
val board: Map[String, Node] = ???
val occupiedSquares = board.keySet
...
val occupiedInRow =
occupiedSquares filter {_ startsWith myRow}
val occupiedInCol =
occupiedSquares filter {_ endsWith myCol}