>
Mark Bastian
2/25/2015
“In computer science, functional programming is a programming paradigm, a style of building the structure and elements of computer programs, that treats computation as the evaluation of mathematical functions and avoids changing-state and mutable data.”http://en.wikipedia.org/wiki/Functional_programming
Map a collection to its squares
public static List<Integer>map(List<Integer> input){
List<Integer> output = new LinkedList<Integer>();
for(Integer i : input){
output.add(i * i);
}
return output;
}
Map a collection to its doubles
public static List<Integer>map(List<Integer> input){
List<Integer> output = new LinkedList<Integer>();
for(Integer i : input){
output.add(i * 2);
}
return output;
}
public interface IOperation<T, U> {
public T apply(U input);
}
public static <T, U> List<T> map(List<U> input, IOperation<T, U> operation){
List<T> output = new LinkedList<T>();
for(U i : input){
output.add(operation.apply(i));
}
return output;
}
Whoa, ugly!
public static void main(String[] args){
List<Integer> ints = new LinkedList<Integer>();
for(int i = 0; i < 1000000; i++) ints.add(i);
List<Double> roots = map(ints, new IOperation<Double, Integer>() {
@Override
public Double apply(Integer input) {
return Math.sqrt(input);
}
});
System.out.println(roots.size());
}
This isn't particularly satisfying
Java 8
List<Integer> squares =
ints.stream().map(i -> i * i).collect(Collectors.toList());
Java 8
List<Integer> squares =
ints.stream().map(i -> i * i).collect(Collectors.toList());
Java 7
List<Double> squares = map(ints, new IOperation<Double, Integer>() {
@Override
public Double apply(Integer input) {
return input * input;
}
});
Java 8
List<Integer> squares =
ints.stream().map(i -> i * i).collect(Collectors.toList());
Scala
val squares = (0 until 1000000) map { x => x * x }
Clojure
(def squares (map #(* % %) (range 0 1000000)))
Scala
val squares = (0 until 1000000) map { x => x * x }
Clojure
(def squares (map #(* % %) (range 0 1000000)))
The FP versions are even smaller than the Java 8 version because of their design
What happens here?
List<Integer> ints = new LinkedList<>();
for(int i = 0; i < 1000000; i++) ints.add(i);
new Thread(() -> {
while(!ints.isEmpty()) ints.remove(0);
}).start();
List<Integer> evens =
ints.stream().filter(i -> (0 == (i % 2))).collect(Collectors.toList());
System.out.println(evens.size());
Exception in thread "main" java.lang.NullPointerException
at example.StreamStuffNPE.lambda$main$1(StreamStuffNPE.java:17)
at example.StreamStuffNPE$$Lambda$2/250421012.test(Unknown Source)
at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:174)
at java.util.LinkedList$LLSpliterator.forEachRemaining(LinkedList.java:1235)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:512)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:502)
at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
at example.StreamStuffNPE.main(StreamStuffNPE.java:17)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)
What happens here?
(def int-vec (into [] (range 1000000)))
(future
(loop[rem (rest int-vec)]
(if (empty? rem)
(prn "All done")
(recur (rest rem)))))
(count (filter #(= 0 (mod % 2)) int-vec))
=> #'user/int-vec
=> #<core$future_call$reify__6320@73f5597d: :pending>
"All done"
=> 500000
Car.java
package cars;
public class Car {
private String make = null;
private int year = 0;
private String color = null;
private double topSpeed = 0.0;
public String getMake() {
return make;
}
public void setMake(String make) {
this.make = make;
}
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public double getTopSpeed() {
return topSpeed;
}
public void setTopSpeed(double topSpeed) {
this.topSpeed = topSpeed;
}
@Override
public String toString() {
return getColor() + " " + getYear() + " " + getMake() +
" with top speed of " + getTopSpeed() + " mph";
}
}
TestDriver.java
package cars;
public class TestDriver {
//Super complicated ranking function.
public static double rank(Car car){
double score = 0.0;
switch (car.getColor()) {
case "Red":
score = 2.0;
break;
case "Green":
score = 1.0;
break;
case "White":
score = 1.5;
break;
case "Black":
score = 1.9;
break;
case "Beige":
score = 0.0;
break;
}
return score;
}
}
CarPurchasingModel.java
package cars;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
public class CarPurchasingModel {
public static void main(String[] args){
List<String> colors = new LinkedList<>();
colors.add("Red");
colors.add("Green");
colors.add("White");
colors.add("Black");
colors.add("Beige");
Map<Car, Double> ranked = new HashMap<Car, Double>();
for(String color : colors){
Car car = new Car();
car.setColor(color);
car.setMake("Honda");
car.setYear(2015);
car.setTopSpeed(120.0);
ranked.put(car, TestDriver.rank(car));
}
Car bestCar = null;
double bestScore = -1;
for(Map.Entry<Car, Double> entry : ranked.entrySet()){
if(bestCar == null || entry.getValue() > bestScore){
bestCar = entry.getKey();
bestScore = entry.getValue();
}
}
System.out.println("The best car is: " + bestCar);
}
}
(def color-options [:red :green :white :black :beige])
(def basic-car { :make "Honda" :year 2015 :color :beige :top-speed 120.0 })
(def all-cars (map #(into basic-car { :color % }) color-options))
(defn rank [item]
"Rank the car using a very complicated user preference algorithm"
(case (item :color)
:red 2.0
:green 1.0
:white 1.5
:beige 0.0
:black 1.9
0.0))
(def rank-map (zipmap all-cars (map rank all-cars)))
(prn (str "The best car is: " (apply max-key val rank-map)))
IColorData.java
package cars2;
public interface IColorData {
public String getColor();
}
ImmutableCar.java
package cars2;
public class ImmutableCar implements IColorData {
private final String make;
private final int year;
private final String color;
private final double topSpeed;
public ImmutableCar(String make, int year, String color, double topSpeed) {
this.make = make;
this.year = year;
this.color = color;
this.topSpeed = topSpeed;
}
public String getMake() {
return make;
}
public int getYear() {
return year;
}
public String getColor() {
return color;
}
public double getTopSpeed() {
return topSpeed;
}
public ImmutableCar setMake(String newMake){
return new ImmutableCar(newMake, getYear(), getColor(), getTopSpeed());
}
public ImmutableCar setYear(int newYear){
return new ImmutableCar(getMake(), newYear, getColor(), getTopSpeed());
}
public ImmutableCar setColor(String newColor){
return new ImmutableCar(getMake(), getYear(), newColor, getTopSpeed());
}
public ImmutableCar setTopSpeed(double newTopSpeed){
return new ImmutableCar(getMake(), getYear(), getColor(), newTopSpeed);
}
@Override
public String toString() {
return getColor() + " " + getYear() + " " + getMake() + " with top speed of " + getTopSpeed() + " mph";
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ImmutableCar that = (ImmutableCar) o;
if (Double.compare(that.topSpeed, topSpeed) != 0) return false;
if (year != that.year) return false;
if (color != null ? !color.equals(that.color) : that.color != null) return false;
if (make != null ? !make.equals(that.make) : that.make != null) return false;
return true;
}
@Override
public int hashCode() {
int result;
long temp;
result = make != null ? make.hashCode() : 0;
result = 31 * result + year;
result = 31 * result + (color != null ? color.hashCode() : 0);
temp = Double.doubleToLongBits(topSpeed);
result = 31 * result + (int) (temp ^ (temp >>> 32));
return result;
}
}
TestDriver2.java
package cars2;
public class TestDriver2 {
//Super complicated ranking function.
public static double rank(IColorData car){
double score = 0.0;
switch (car.getColor()) {
case "Red":
score = 2.0;
break;
case "Green":
score = 1.0;
break;
case "White":
score = 1.5;
break;
case "Black":
score = 1.9;
break;
case "Beige":
score = 0.0;
break;
}
return score;
}
}
ImprovedCarPurchasingModel.java
package cars2;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
public class ImprovedCarPurchasingModel {
public static void main(String[] args){
ImmutableCar defaultCar = new ImmutableCar("Honda", 2105, "Beige", 120.0);
List<String> colors = new LinkedList<>();
colors.add("Red");
colors.add("Green");
colors.add("White");
colors.add("Black");
colors.add("Beige");
Map<ImmutableCar, Double> ranked = new HashMap<ImmutableCar, Double>();
for(String color : colors){
ImmutableCar car = defaultCar.setColor(color);
ranked.put(car, TestDriver2.rank(car));
}
ImmutableCar bestCar = null;
double bestScore = -1;
for(Map.Entry<ImmutableCar, Double> entry : ranked.entrySet()){
if(bestCar == null || entry.getValue() > bestScore){
bestCar = entry.getKey();
bestScore = entry.getValue();
}
}
System.out.println("The best car is: " + bestCar);
}
}
Demonstrating FP in a stateful world
rules
(ns conway.rules)
(defn gen-cell[](if (> (Math/random) 0.7) :alive :dead))
(defn seed-grid [rows cols]
(vec (take rows (repeatedly (fn [] (vec (take cols (repeatedly gen-cell))))))))
(defn neighbors [[i j]]
(let [x ((juxt inc inc identity dec dec dec identity inc) i)
y ((juxt identity inc inc inc identity dec dec dec) j)]
(map vector x y)))
(defn count-neighbors [grid coord]
(let [n (map #(get-in grid %) (neighbors coord))]
(count (filter #(= % :alive) n))))
(defn sim-step [grid coord]
(let [n-live (count-neighbors grid coord)]
(if (= :alive (get-in grid coord))
(case n-live
(2 3) :alive
:dead)
(if (= 3 n-live) :alive :dead))))
(defn step [grid]
(into [] (for [i (range (count grid))]
(into [] (for [j (range (count (get grid i)))]
(sim-step grid [i j]))))))
ui
(ns conway.core
(:gen-class)
(:require [conway.rules])
(:import (javax.swing JFrame JPanel JButton JCheckBox Box BoxLayout)
(java.awt BorderLayout Graphics2D Color ImageCapabilities)
(java.awt.geom Rectangle2D$Double)
(java.awt.event ActionListener)
(java.awt.image VolatileImage)))
(defn panel [grid-ref]
(let [pnl (proxy [JPanel] []
(paint [g]
(let [w (.getWidth this)
h (.getHeight this)
bg (Rectangle2D$Double. 0 0 w (.getHeight this))
vimg (.createVolatileImage this w h (ImageCapabilities. true))
g2d (.createGraphics vimg)]
(doto g2d
(.setPaint Color/BLACK)
(.fill bg))
(doseq [i (range (count @grid-ref))]
(doseq [j (range (count (get @grid-ref i)))]
(let [c (if (= :alive (get-in @grid-ref [i j])) Color/GREEN Color/RED)
sq (Rectangle2D$Double. (* i (/ w (count @grid-ref)))
(* j (/ h (count (get @grid-ref i))))
(/ w (count @grid-ref))
(/ h (count (get @grid-ref i))))]
(do
(.setPaint g2d c)
(.fill g2d sq)))))
(.drawImage g vimg 0 0 this)
)))]
(do
(add-watch grid-ref :repaint (fn [_ _ _ _] (.repaint pnl)))
pnl)))
(defn sim-button [grid-ref]
(let [cb (JCheckBox. "Sim!")]
(future
(loop []
(do
(when (-> cb .isSelected)
(dosync (ref-set grid-ref (conway.rules/step @grid-ref))))
(Thread/sleep 33))
(recur)))
cb))
(defn step-button [grid-ref]
(doto (JButton. "Step")
(.addActionListener
(reify ActionListener
(actionPerformed [_ _]
(dosync (ref-set grid-ref (conway.rules/step @grid-ref))))))))
(defn reset-button [grid-ref]
(doto (JButton. "Reset")
(.addActionListener
(reify ActionListener
(actionPerformed [_ _]
(dosync (ref-set grid-ref (conway.rules/seed-grid 100 100))))))))
(defn run-app [exit-op] (let [grid-ref (ref (conway.rules/seed-grid 100 100))]
(doto (JFrame. "Conway's Game of Life")
(.setSize 800 600)
(.setDefaultCloseOperation exit-op)
(.add (panel grid-ref) BorderLayout/CENTER)
(.add (doto (Box. BoxLayout/PAGE_AXIS)
(.add (step-button grid-ref))
(.add (sim-button grid-ref))
(.add (reset-button grid-ref))) BorderLayout/SOUTH)
(.setVisible true))))
;(run-app JFrame/DISPOSE_ON_CLOSE)
(defn -main
[& args]
(future (run-app JFrame/EXIT_ON_CLOSE)))