├── functional_thinking_examples ├── 1_08_fibs.clj ├── 1_07_heads.clj ├── groovy │ ├── polydispatch │ │ ├── Product.groovy │ │ ├── Multiply.groovy │ │ ├── Incrementation.groovy │ │ ├── ProductFactory.groovy │ │ ├── LetterGradeTest.groovy │ │ ├── LetterGrade.groovy │ │ └── ProductTest.groovy │ ├── NumberClassifyInteger.groovy │ ├── template │ │ ├── CustomerBlocks.groovy │ │ ├── Customer.groovy │ │ └── CustomerBlocksWithProtection.groovy │ ├── as_demo.groovy │ ├── make_counter.groovy │ ├── trans │ │ └── TheCompanyProcess.groovy │ ├── palindromes │ │ └── Palindromes.groovy │ ├── partial.groovy │ ├── memoize_hashing │ │ └── NameHash.groovy │ ├── camelize │ │ ├── Camelize.groovy │ │ └── CamelizeTest.groovy │ ├── transformation_primes │ │ └── PrimeNumberClassifier.groovy │ ├── camelize_category │ │ ├── CamelizeCategory.groovy │ │ └── CamelizeCategoryTest.groovy │ ├── easyb │ │ ├── it.groovy │ │ ├── scenario.groovy │ │ └── behaviorcategory.groovy │ ├── complexnums │ │ ├── ComplexNumber.groovy │ │ └── ComplexNumberTest.groovy │ ├── number_classifier │ │ ├── NumberClassifier.groovy │ │ └── NumberClassifierOpt.groovy │ ├── allaboutlists │ │ ├── LazyList.groovy │ │ ├── NumberClassifier.groovy │ │ └── AllAboutListsTest.groovy │ ├── memoization │ │ ├── ClassifierMemoizedSum.groovy │ │ ├── ClassifierMemoized.groovy │ │ ├── ClassifierCached.groovy │ │ ├── ClassifierCachedSum.groovy │ │ └── ClassifierTest.groovy │ ├── patterns_adaptor │ │ ├── pegs.groovy │ │ ├── pegs_test.groovy │ │ └── pegs_closures_test.groovy │ ├── open_classes │ │ └── test_string_open_class.groovy │ ├── camelize_expando │ │ └── TestCamelizeExpando.groovy │ ├── simple_closure │ │ └── SimpleClosure.groovy │ ├── iterate_recurse.groovy │ ├── currying.groovy │ ├── strategy │ │ └── StrategyTest.groovy │ ├── list_perspective │ │ └── ListPerspectiveDemo.groovy │ ├── lazy_primes │ │ └── LazyBuildup.groovy │ ├── expandounits │ │ └── IntegerConversion.groovy │ ├── FlyweightTest.groovy │ └── metafunctionaljava │ │ └── IntegerClassifierTest.groovy ├── java │ ├── patterns_adaptor │ │ ├── Circularity.java │ │ ├── SquarePeg.java │ │ ├── RoundPeg.java │ │ ├── RoundHole.java │ │ ├── SquarePegAdaptor.java │ │ └── AdaptorTest.java │ ├── composition │ │ ├── FPrime.java │ │ ├── PrimeBeta.java │ │ ├── Factors.java │ │ ├── FClassifier.java │ │ ├── FactorsBeta.java │ │ ├── ClassifierBeta.java │ │ ├── PrimeAlpha.java │ │ └── ClassifierAlpha.java │ ├── laziness │ │ └── PrimeIterator.java │ ├── number_classifier_functional_java │ │ ├── NumberStuff.java │ │ ├── NumberClassifier.java │ │ └── NumberClassifierFJOptimized.java │ ├── trans │ │ └── TheCompanyProcess.java │ ├── errorhandling │ │ ├── Either.java │ │ ├── RomanNumeralParser.java │ │ ├── FjRomanNumeralParser.java │ │ ├── EitherTest.java │ │ ├── RomanNumeral.java │ │ └── FjEitherTest.java │ ├── transformations_functionaljava │ │ └── PrimeNumberClassifier.java │ ├── trans8 │ │ └── Process.java │ ├── wordfreq │ │ └── Words.java │ ├── oft_primes │ │ └── Prime.java │ ├── structuralpatternmatching │ │ ├── Tree.java │ │ ├── Trees.java │ │ └── TreeTest.java │ ├── number_classifier_totally_lazy │ │ └── NumberClassifier.java │ ├── wordfreq8 │ │ └── Words.java │ ├── number_classifier │ │ ├── ImpNumberClassifierSimple.java │ │ ├── ImpNumberClassifier.java │ │ ├── NumberClassifier.java │ │ └── NumberClassifierOptimized.java │ ├── number_classifier8 │ │ └── NumberClassifier.java │ └── CounterDemo.java ├── scala │ ├── palimdromes │ │ └── Palindromes.scala │ ├── transformation_primes │ │ └── PrimeNumberClassifier.scala │ ├── currying │ │ └── CurryTest.scala │ ├── number_classifier │ │ └── NumberClassifier.scala │ ├── complex_numbers │ │ ├── ComplexTest.scala │ │ └── Complex.scala │ ├── pattern_matching │ │ └── Colors.scala │ ├── trans │ │ └── Process.scala │ ├── letter_grades │ │ └── LetterGrade.scala │ └── indexOfAny │ │ └── indexOfAny.scala ├── 1_06_step6.clj ├── 1_05_step5.java ├── clojure │ ├── primes │ │ └── core.clj │ ├── letter-grades │ │ ├── lettergrades.clj │ │ └── lettergradestest.clj │ ├── palindromes │ │ └── palindromes.clj │ ├── color_dispatch │ │ ├── core_test.clj │ │ └── core.clj │ ├── trans │ │ └── core.clj │ ├── name-hash │ │ └── core.clj │ └── number_classifier │ │ └── core.clj ├── 1_04_step4.java ├── ruby │ └── transformation │ │ └── prime_number_classifier.rb ├── 1_03_step3.java ├── 1_02_step1.java └── 1_01_indexOfAny.java └── README.md /functional_thinking_examples/1_08_fibs.clj: -------------------------------------------------------------------------------- 1 | ; Fibonacci numbers pass 1000 at n=17 2 | (first 3 | (index-filter #(> % 1000) (fibo))) 4 | -> 17 -------------------------------------------------------------------------------- /functional_thinking_examples/1_07_heads.clj: -------------------------------------------------------------------------------- 1 | ; indexes of heads in stream of coin flips 2 | (index-filter #{:h} [:t :t :h :t :h :t :t :t :h :h]) 3 | -> (2 4 8 9) -------------------------------------------------------------------------------- /functional_thinking_examples/groovy/polydispatch/Product.groovy: -------------------------------------------------------------------------------- 1 | package com.nealford.ft.polydispatch 2 | 3 | interface Product { 4 | public int evaluate(int op1, int op2) 5 | } 6 | -------------------------------------------------------------------------------- /functional_thinking_examples/java/patterns_adaptor/Circularity.java: -------------------------------------------------------------------------------- 1 | package com.nealford.ft.adaptor; 2 | 3 | public interface Circularity { 4 | public double getRadius(); 5 | } 6 | -------------------------------------------------------------------------------- /functional_thinking_examples/scala/palimdromes/Palindromes.scala: -------------------------------------------------------------------------------- 1 | 2 | 3 | def isPalindrome(x: String) = x == x.reverse 4 | 5 | def findPalidrome(s: Seq[String]) = s find isPalindrome 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /functional_thinking_examples/1_06_step6.clj: -------------------------------------------------------------------------------- 1 | // Lispify 2 | (defn index-filter [pred coll] 3 | (when pred <1> 4 | (for [[index element] (indexed coll) :when (pred element)] index))) <2> -------------------------------------------------------------------------------- /functional_thinking_examples/1_05_step5.java: -------------------------------------------------------------------------------- 1 | // add comprehension 2 | indexOfAny(str, searchChars) { 3 | when(searchChars) { 4 | for ([i, ch] in indexed(str)) { <1> 5 | when (searchChars(ch)) i; 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /functional_thinking_examples/groovy/polydispatch/Multiply.groovy: -------------------------------------------------------------------------------- 1 | package com.nealford.ft.polydispatch 2 | 3 | class Multiply implements Product { 4 | @Override 5 | int evaluate(int op1, int op2) { 6 | op1 * op2 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /functional_thinking_examples/clojure/primes/core.clj: -------------------------------------------------------------------------------- 1 | (ns primes.core) 2 | 3 | (defn factors [n] 4 | (filter #(zero? (rem n %)) (range 1 (inc n)))) 5 | 6 | (defn sum-factors [n] 7 | (reduce + (factors n))) 8 | 9 | (defn prime? [n] 10 | (= (inc n) (sum-factors n))) -------------------------------------------------------------------------------- /functional_thinking_examples/groovy/polydispatch/Incrementation.groovy: -------------------------------------------------------------------------------- 1 | package com.nealford.ft.polydispatch 2 | 3 | class Incrementation implements Product { 4 | @Override 5 | int evaluate(int op1, int op2) { 6 | def sum = 0 7 | op2.times { 8 | sum += op1 9 | } 10 | sum 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /functional_thinking_examples/1_04_step4.java: -------------------------------------------------------------------------------- 1 | // when clause for innermost for 2 | indexOfAny(str, searchChars) { 3 | when(searchChars) { 4 | csLen = str.length(); 5 | for (i = 0; i < csLen; i++) { 6 | ch = str.charAt(i); 7 | when (searchChars(ch)) i; 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /functional_thinking_examples/groovy/polydispatch/ProductFactory.groovy: -------------------------------------------------------------------------------- 1 | package com.nealford.ft.polydispatch 2 | 3 | class ProductFactory { 4 | static Product getProduct(int maxNumber) { 5 | if (maxNumber > 10000) 6 | return new Multiply() 7 | else 8 | return new Incrementation() 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /functional_thinking_examples/groovy/NumberClassifyInteger.groovy: -------------------------------------------------------------------------------- 1 | Integer.metaClass.isPerfect = {-> 2 | Classifier.isPerfect(delegate) 3 | } 4 | 5 | Integer.metaClass.isAbundant = {-> 6 | Classifier.isAbundant(delegate) 7 | } 8 | 9 | Integer.metaClass.isDeficient = {-> 10 | Classifier.isDeficient(delegate) 11 | } 12 | -------------------------------------------------------------------------------- /functional_thinking_examples/java/patterns_adaptor/SquarePeg.java: -------------------------------------------------------------------------------- 1 | package com.nealford.ft.adaptor; 2 | 3 | public class SquarePeg { 4 | private int width; 5 | 6 | public SquarePeg(int width) { 7 | this.width = width; 8 | } 9 | 10 | public int getWidth() { 11 | return width; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /functional_thinking_examples/java/patterns_adaptor/RoundPeg.java: -------------------------------------------------------------------------------- 1 | package com.nealford.ft.adaptor; 2 | 3 | public class RoundPeg implements Circularity { 4 | private double radius; 5 | 6 | public double getRadius() { 7 | return radius; 8 | } 9 | 10 | public RoundPeg(int radius) { 11 | this.radius = radius; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /functional_thinking_examples/groovy/template/CustomerBlocks.groovy: -------------------------------------------------------------------------------- 1 | package templates; 2 | 3 | class CustomerBlocks { 4 | def plan, checkCredit, checkInventory, ship 5 | 6 | def CustomerBlocks() { 7 | plan = [] 8 | } 9 | 10 | def process() { 11 | checkCredit() 12 | checkInventory() 13 | ship() 14 | } 15 | } -------------------------------------------------------------------------------- /functional_thinking_examples/ruby/transformation/prime_number_classifier.rb: -------------------------------------------------------------------------------- 1 | class PrimeNumberClassifier 2 | def self.factors(num) 3 | (1..num).select { |i| num % i == 0 } 4 | end 5 | 6 | def self.sum_factors(num) 7 | factors(num).reduce(0, :+) 8 | end 9 | 10 | def self.prime?(num) 11 | (sum_factors(num) == num + 1) 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /functional_thinking_examples/groovy/as_demo.groovy: -------------------------------------------------------------------------------- 1 | h = [hasNext: { h.i > 0 }, next: {h.i--}] 2 | h.i = 10 // <1> 3 | def pseudoIterator = h as Iterator // <2> 4 | 5 | while (pseudoIterator.hasNext()) 6 | print pseudoIterator.next() + (pseudoIterator.hasNext() ? ", " : "\n") 7 | // 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 8 | -------------------------------------------------------------------------------- /functional_thinking_examples/groovy/make_counter.groovy: -------------------------------------------------------------------------------- 1 | def Closure makeCounter() { 2 | def local_variable = 0 3 | return { return local_variable += 1 } // <1> 4 | } 5 | 6 | c1 = makeCounter() // <2> 7 | c1() // <3> 8 | c1() 9 | c1() 10 | 11 | c2 = makeCounter() // <4> 12 | 13 | println "C1 = ${c1()}, C2 = ${c2()}" 14 | // output: C1 = 4, C2 = 1 // <5> 15 | -------------------------------------------------------------------------------- /functional_thinking_examples/java/patterns_adaptor/RoundHole.java: -------------------------------------------------------------------------------- 1 | package com.nealford.ft.adaptor; 2 | 3 | public class RoundHole { 4 | private double radius; 5 | 6 | public RoundHole(double radius) { 7 | this.radius = radius; 8 | } 9 | 10 | public boolean pegFits(Circularity peg) { 11 | return peg.getRadius() <= radius; 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /functional_thinking_examples/groovy/trans/TheCompanyProcess.groovy: -------------------------------------------------------------------------------- 1 | package com.nealford.functionalthinking.trans 2 | 3 | class TheCompanyProcess { 4 | // BEGIN groovy_process 5 | public static String cleanUpNames(listOfNames) { 6 | listOfNames 7 | .findAll { it.length() > 1 } 8 | .collect { it.capitalize() } 9 | .join ',' 10 | } 11 | // END groovy_process 12 | 13 | } 14 | -------------------------------------------------------------------------------- /functional_thinking_examples/scala/transformation_primes/PrimeNumberClassifier.scala: -------------------------------------------------------------------------------- 1 | package com.nealford.functionalthinking.transformations 2 | 3 | object PrimeNumberClassifier { 4 | def factors(number: Int) = 5 | (1 to number) filter (number % _ == 0) 6 | 7 | def sum(factors: Seq[Int]) = 8 | factors.sum 9 | 10 | def isPrime(number: Int) = 11 | number == 2 || sum(factors(number)) == number + 1 12 | } -------------------------------------------------------------------------------- /functional_thinking_examples/java/patterns_adaptor/SquarePegAdaptor.java: -------------------------------------------------------------------------------- 1 | package com.nealford.ft.adaptor; 2 | 3 | public class SquarePegAdaptor implements Circularity { 4 | private SquarePeg peg; 5 | 6 | public SquarePegAdaptor(SquarePeg peg) { 7 | this.peg = peg; 8 | } 9 | 10 | public double getRadius() { 11 | return Math.sqrt(Math.pow((peg.getWidth()/2), 2) * 2); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /functional_thinking_examples/groovy/template/Customer.groovy: -------------------------------------------------------------------------------- 1 | package templates; 2 | 3 | abstract class Customer { 4 | def plan 5 | 6 | def Customer() { 7 | plan = [] 8 | } 9 | 10 | def abstract checkCredit() 11 | def abstract checkInventory() 12 | def abstract ship() 13 | 14 | def process() { 15 | checkCredit() 16 | checkInventory() 17 | ship() 18 | } 19 | } -------------------------------------------------------------------------------- /functional_thinking_examples/groovy/palindromes/Palindromes.groovy: -------------------------------------------------------------------------------- 1 | def isPalindrome(s) { 2 | def sl = s.toLowerCase() 3 | sl == sl.reverse() 4 | } 5 | 6 | def findFirstPalindrome(s) { 7 | s.tokenize(' ').find {isPalindrome(it)} 8 | } 9 | 10 | s1 = "The quick brown fox jumped over anna the dog"; 11 | println(findFirstPalindrome(s1)) 12 | s2 = "Bob went to Harrah and gambled with Otto and Steve" 13 | println(findFirstPalindrome(s2)) -------------------------------------------------------------------------------- /functional_thinking_examples/clojure/letter-grades/lettergrades.clj: -------------------------------------------------------------------------------- 1 | (ns lettergrades) 2 | 3 | (defn in [score low high] 4 | (and (number? score) (<= low score high))) 5 | 6 | (defn letter-grade [score] 7 | (cond 8 | (in score 90 100) "A" 9 | (in score 80 90) "B" 10 | (in score 70 80) "C" 11 | (in score 60 70) "D" 12 | (in score 0 60) "F" 13 | (re-find #"[ABCDFabcdf]" score) (.toUpperCase score))) 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Functional Thinking 2 | =================== 3 | 4 | This is the example code than accompanies Functional Thinking by Neal Ford (9781449365516). 5 | 6 | Click the Download Zip button to the right to download example code. 7 | 8 | Visit the catalog page [here](http://shop.oreilly.com/product/0636920029687.do). 9 | 10 | See an error? Report it [here](http://oreilly.com/catalog/errata.csp?isbn=0636920029687), or simply fork and send us a pull request. 11 | -------------------------------------------------------------------------------- /functional_thinking_examples/groovy/template/CustomerBlocksWithProtection.groovy: -------------------------------------------------------------------------------- 1 | package templates; 2 | 3 | class CustomerBlocksWithProtection { 4 | def plan, checkCredit, checkInventory, ship 5 | 6 | def CustomerBlocksWithProtection() { 7 | plan = [] 8 | } 9 | // BEGIN groovy_customer_blocks 10 | def process() { 11 | checkCredit?.call() 12 | checkInventory?.call() 13 | ship?.call() 14 | } 15 | // END groovy_customer_blocks 16 | } -------------------------------------------------------------------------------- /functional_thinking_examples/groovy/partial.groovy: -------------------------------------------------------------------------------- 1 | def volume = { h, w, l -> return h * w * l } 2 | def area = volume.curry(1) 3 | def lengthPA = volume.curry(1, 1) // <1> 4 | def lengthC = volume.curry(1).curry(1) // <2> 5 | 6 | println "The volume of the 2x3x4 rectangular solid is ${volume(2, 3, 4)}" 7 | println "The area of the 3x4 rectangle is ${area(3, 4)}" 8 | println "The length of the 6 line is ${lengthPA(6)}" 9 | println "The length of the 6 line via curried function is ${lengthC(6)}" -------------------------------------------------------------------------------- /functional_thinking_examples/scala/currying/CurryTest.scala: -------------------------------------------------------------------------------- 1 | object CurryTest extends App { 2 | 3 | def filter(xs: List[Int], p: Int => Boolean): List[Int] = 4 | if (xs.isEmpty) xs 5 | else if (p(xs.head)) xs.head :: filter(xs.tail, p) 6 | else filter(xs.tail, p) 7 | 8 | def dividesBy(n: Int)(x: Int) = ((x % n) == 0) // <1> 9 | 10 | val nums = List(1, 2, 3, 4, 5, 6, 7, 8) 11 | println(filter(nums, dividesBy(2))) // <2> 12 | println(filter(nums, dividesBy(3))) 13 | } -------------------------------------------------------------------------------- /functional_thinking_examples/groovy/memoize_hashing/NameHash.groovy: -------------------------------------------------------------------------------- 1 | package com.nealford.javanext.memoizehashing 2 | 3 | class NameHash { 4 | def static hash = {name -> 5 | name.collect{rot13(it)}.join() 6 | }.memoize() 7 | 8 | public static char rot13(s) { 9 | char c = s 10 | switch (c) { 11 | case 'A'..'M': 12 | case 'a'..'m': return c + 13 13 | case 'N'..'Z': 14 | case 'n'..'z': return c - 13 15 | default: return c 16 | } 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /functional_thinking_examples/java/composition/FPrime.java: -------------------------------------------------------------------------------- 1 | package com.nealford.conf.ft.composition; 2 | 3 | import java.util.Set; 4 | 5 | // BEGIN java_fprime 6 | public class FPrime { 7 | 8 | public static boolean isPrime(int number) { 9 | Set factors = Factors.of(number); 10 | return number > 1 && 11 | factors.size() == 2 && 12 | factors.contains(1) && 13 | factors.contains(number); 14 | } 15 | } 16 | // END java_fprime 17 | -------------------------------------------------------------------------------- /functional_thinking_examples/groovy/camelize/Camelize.groovy: -------------------------------------------------------------------------------- 1 | package com.nealford.functionalthinking 2 | 3 | // BEGIN groovy_camelize_category 4 | @Category(String) class Camelize { 5 | def getCamelize() { 6 | def cString = this.split("_").collect() { s -> 7 | s.substring(0, 1).toUpperCase() + 8 | s.substring(1, s.length()) 9 | }.join() 10 | cString.substring(0, 1).toLowerCase() + 11 | cString.substring(1, cString.length()) 12 | } 13 | } 14 | // END groovy_camelize_category -------------------------------------------------------------------------------- /functional_thinking_examples/groovy/polydispatch/LetterGradeTest.groovy: -------------------------------------------------------------------------------- 1 | import org.junit.Test 2 | import com.nealford.ft.polydispatch.LetterGrade 3 | 4 | import static org.junit.Assert.assertEquals 5 | 6 | class LetterGradeTest { 7 | @Test 8 | public void test_letter_grades() { 9 | def lg = new LetterGrade() 10 | assertEquals("A", lg.gradeFromScore(92)) 11 | assertEquals("B", lg.gradeFromScore(85)) 12 | assertEquals("D", lg.gradeFromScore(65)) 13 | assertEquals("F", lg.gradeFromScore("f")) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /functional_thinking_examples/groovy/polydispatch/LetterGrade.groovy: -------------------------------------------------------------------------------- 1 | package com.nealford.ft.polydispatch 2 | 3 | class LetterGrade { 4 | def gradeFromScore(score) { 5 | switch (score) { 6 | case 90..100 : return "A" 7 | case 80..<90 : return "B" 8 | case 70..<80 : return "C" 9 | case 60..<70 : return "D" 10 | case 0..<60 : return "F" 11 | case ~"[ABCDFabcdf]" : return score.toUpperCase() 12 | default: throw new IllegalArgumentException("Invalid score: ${score}") 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /functional_thinking_examples/clojure/palindromes/palindromes.clj: -------------------------------------------------------------------------------- 1 | (defn palindrome? [s] 2 | (let [sl (.toLowerCase s)] 3 | (= sl (apply str (reverse sl))))) 4 | 5 | (defn find-palindromes [s] 6 | (filter palindrome? (clojure.string/split s #" "))) 7 | 8 | (println (find-palindromes "The quick brown fox jumped over anna the dog")) 9 | ; (anna) 10 | (println (find-palindromes "Bob went to Harrah and gambled with Otto and Steve")) 11 | ;(Bob Harrah Otto) 12 | (println (take 1 (find-palindromes "Bob went to Harrah with Otto and Steve"))) 13 | ;(Bob) -------------------------------------------------------------------------------- /functional_thinking_examples/clojure/color_dispatch/core_test.clj: -------------------------------------------------------------------------------- 1 | (ns color-dispatch.core-test 2 | (:require [clojure.test :refer :all] 3 | [color-dispatch.core :refer :all])) 4 | 5 | (deftest pure-colors 6 | (is (= "Red: 5" (color-string (struct color 5 0 0)))) 7 | (is (= "Green: 12" (color-string (struct color 0 12 0)))) 8 | (is (= "Blue: 40" (color-string (struct color 0 0 40))))) 9 | 10 | (deftest varied-colors 11 | (is (= "Red:5, Green: 40, Blue: 6" (color-string (struct color 5 40 6))))) 12 | 13 | (run-all-tests) 14 | 15 | -------------------------------------------------------------------------------- /functional_thinking_examples/groovy/transformation_primes/PrimeNumberClassifier.groovy: -------------------------------------------------------------------------------- 1 | package com.nealford.functionalthinking.transformations 2 | 3 | class PrimeNumberClassifier { 4 | private int number; 5 | 6 | PrimeNumberClassifier(int number) { 7 | this.number = number 8 | } 9 | 10 | public def getFactors() { 11 | (1..number).findAll { number % it == 0 }.toSet() 12 | } 13 | 14 | public def sumFactors() { 15 | getFactors().sum() 16 | } 17 | 18 | public def isPrime() { 19 | sumFactors() == number + 1 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /functional_thinking_examples/java/composition/PrimeBeta.java: -------------------------------------------------------------------------------- 1 | package com.nealford.conf.ft.composition; 2 | 3 | import java.util.HashSet; 4 | import java.util.Set; 5 | 6 | // BEGIN java_prime_beta 7 | public class PrimeBeta extends FactorsBeta { 8 | public PrimeBeta(int number) { 9 | super(number); 10 | } 11 | 12 | public boolean isPrime() { 13 | Set primeSet = new HashSet() {{ 14 | add(1); add(number);}}; 15 | return getFactors().equals(primeSet); 16 | } 17 | 18 | } 19 | // END java_prime_beta 20 | -------------------------------------------------------------------------------- /functional_thinking_examples/groovy/camelize_category/CamelizeCategory.groovy: -------------------------------------------------------------------------------- 1 | package com.nealford.functionalthinking 2 | 3 | // BEGIN groovy_camelize_category_retro 4 | public class CamelizeCategory { 5 | static def camelize(String self) { // <1> 6 | def newName = self.split("_").collect() { s -> 7 | s.substring(0, 1).toUpperCase() + 8 | s.substring(1, s.length()) 9 | }.join() 10 | newName.substring(0, 1).toLowerCase() + 11 | newName.substring(1, newName.length()) 12 | } 13 | // END groovy_camelize_category_retro 14 | } 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /functional_thinking_examples/clojure/trans/core.clj: -------------------------------------------------------------------------------- 1 | (ns trans.core 2 | (:require [clojure.string :as s])) 3 | 4 | ; BEGIN clj_process 5 | (defn process [list-of-emps] 6 | (reduce str (interpose "," 7 | (map s/capitalize (filter #(< 1 (count %)) list-of-emps))))) 8 | ; END clj_process 9 | 10 | ; BEGIN clj_process_thread 11 | (defn process2 [list-of-emps] 12 | (->> list-of-emps 13 | (filter #(< 1 (count %))) 14 | (map s/capitalize) 15 | (interpose ",") 16 | (reduce str))) 17 | ; END clj_process_thread 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /functional_thinking_examples/java/laziness/PrimeIterator.java: -------------------------------------------------------------------------------- 1 | package com.nealford.ft.laziness; 2 | 3 | import java.util.Iterator; 4 | 5 | public class PrimeIterator implements Iterator { 6 | private int lastPrime = 1; 7 | 8 | @Override 9 | public boolean hasNext() { 10 | return true; 11 | } 12 | 13 | @Override 14 | public Integer next() { 15 | return lastPrime = Prime.nextPrimeFrom(lastPrime); 16 | } 17 | 18 | @Override 19 | public void remove() { 20 | throw new RuntimeException("Fundamental nature of the universe exception!"); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /functional_thinking_examples/java/number_classifier_functional_java/NumberStuff.java: -------------------------------------------------------------------------------- 1 | package com.nealford.functionalthinking.numberclassifier; 2 | 3 | import fj.F2; 4 | import fj.data.List; 5 | 6 | public class NumberStuff { 7 | // BEGIN java_foldleft_criteria 8 | static public int addOnlyOddNumbersIn(List numbers) { 9 | return numbers.foldLeft(new F2() { 10 | public Integer f(Integer i1, Integer i2) { 11 | return (!(i2 % 2 == 0)) ? i1 + i2 : i1; 12 | } 13 | }, 0); 14 | // END java_foldleft_criteria 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /functional_thinking_examples/groovy/easyb/it.groovy: -------------------------------------------------------------------------------- 1 | def it(spec, closure) { 2 | stepStack.startStep(listener, BehaviorStepType.IT, spec) 3 | closure.delegate = new EnsuringDelegate() 4 | try { 5 | if (beforeIt != null) { 6 | beforeIt() 7 | } 8 | listener.gotResult(new Result(Result.SUCCEEDED)) 9 | use(BehaviorCategory) { 10 | closure() 11 | } 12 | if (afterIt != null) { 13 | afterIt() 14 | } 15 | } catch (Throwable ex) { 16 | listener.gotResult(new Result(ex)) 17 | } 18 | stepStack.stopStep(listener) 19 | } 20 | -------------------------------------------------------------------------------- /functional_thinking_examples/clojure/name-hash/core.clj: -------------------------------------------------------------------------------- 1 | (ns name-hash.core) 2 | (use '[clojure.string :only (join split)]) 3 | 4 | 5 | (let [alpha (into #{} (concat (map char (range (int \a) (inc (int \z)))) 6 | (map char (range (int \A) (inc (int \Z)))))) 7 | rot13-map (zipmap alpha (take 52 (drop 26 (cycle alpha))))] 8 | 9 | (defn rot13 10 | "Given an input string, produce the rot 13 version of 11 | the string. \"hello\" -> \"uryyb\"" 12 | [s] 13 | (apply str (map #(get rot13-map % %) s)))) 14 | 15 | (defn name-hash [name] 16 | (apply str (map #(rot13 %) (split name #"\d")))) 17 | 18 | (def name-hash-m (memoize name-hash)) 19 | 20 | -------------------------------------------------------------------------------- /functional_thinking_examples/groovy/camelize/CamelizeTest.groovy: -------------------------------------------------------------------------------- 1 | package com.nealford.functionalthinking 2 | 3 | import com.nealford.functionalthinking.Camelize 4 | import org.junit.Test 5 | 6 | import static groovy.util.GroovyTestCase.assertEquals 7 | 8 | class CamelizeTest { 9 | 10 | // BEGIN groovy_camelize_test 11 | @Test void can_camelize() { 12 | def expected = [event_map: "eventMap", 13 | name: "name", test_date: "testDate", 14 | test_string_with_breaks: "testStringWithBreaks"] 15 | use(Camelize) { 16 | expected.each { key, value -> 17 | assertEquals value, key.camelize 18 | } 19 | } 20 | } 21 | // END groovy_camelize_test 22 | } 23 | -------------------------------------------------------------------------------- /functional_thinking_examples/clojure/letter-grades/lettergradestest.clj: -------------------------------------------------------------------------------- 1 | (ns nealford-test 2 | (:use clojure.test) 3 | (:use lettergrades)) 4 | 5 | (deftest numeric-letter-grades 6 | (dorun (map #(is (= "A" (letter-grade %))) (range 90 100))) 7 | (dorun (map #(is (= "B" (letter-grade %))) (range 80 89))) 8 | (dorun (map #(is (= "C" (letter-grade %))) (range 70 79))) 9 | (dorun (map #(is (= "D" (letter-grade %))) (range 60 69))) 10 | (dorun (map #(is (= "F" (letter-grade %))) (range 0 59)))) 11 | 12 | (deftest string-letter-grades 13 | (dorun (map #(is (= (.toUpperCase %) 14 | (letter-grade %))) ["A" "B" "C" "D" "F" "a" "b" "c" "d" "f"]))) 15 | 16 | (run-all-tests) 17 | 18 | -------------------------------------------------------------------------------- /functional_thinking_examples/scala/number_classifier/NumberClassifier.scala: -------------------------------------------------------------------------------- 1 | package main.scala 2 | 3 | // BEGIN Number_Classifier_Scala 4 | object NumberClassifier { 5 | def isFactor(factor: Int, number: Int) = 6 | number % factor == 0 7 | 8 | def factors(number: Int) = 9 | (1 to number) filter (isFactor(_, number)) 10 | 11 | def sum(factors : Seq[Int]) = 12 | factors.foldLeft(0)(_ + _) 13 | 14 | def isPerfect(number: Int) = 15 | sum(factors(number)) - number == number 16 | 17 | def isAbundant(number: Int) = 18 | sum(factors(number)) - number > number 19 | 20 | def isDeficient(number: Int) = 21 | sum(factors(number)) - number < number 22 | } 23 | // END Number_Classifier_Scala -------------------------------------------------------------------------------- /functional_thinking_examples/1_03_step3.java: -------------------------------------------------------------------------------- 1 | // remove type decls 2 | indexOfAny(str, searchChars) { 3 | when(searchChars) { 4 | csLen = str.length(); 5 | csLast = csLen - 1; 6 | searchLen = searchChars.length; 7 | searchLast = searchLen - 1; 8 | for (i = 0; i < csLen; i++) { 9 | ch = str.charAt(i); 10 | for (j = 0; j < searchLen; j++) { 11 | if (searchChars[j] == ch) { 12 | if (i < csLast && j < searchLast && CharUtils.isHighSurrogate(ch)) { 13 | if (searchChars[j + 1] == str.charAt(i + 1)) { 14 | return i; 15 | } 16 | } else { 17 | return i; 18 | } 19 | } 20 | } 21 | } 22 | return INDEX_NOT_FOUND; 23 | } 24 | } -------------------------------------------------------------------------------- /functional_thinking_examples/java/patterns_adaptor/AdaptorTest.java: -------------------------------------------------------------------------------- 1 | package com.nealford.ft.adaptor; 2 | 3 | import org.junit.Test; 4 | import static org.junit.Assert.assertFalse; 5 | import static org.junit.Assert.assertTrue; 6 | 7 | public class AdaptorTest { 8 | 9 | // BEGIN java_square_peg_test 10 | @Test 11 | public void square_pegs_in_round_holes() { 12 | RoundHole hole = new RoundHole(4.0); 13 | Circularity peg; 14 | for (int i = 3; i <= 10; i++) { 15 | peg = new SquarePegAdaptor(new SquarePeg(i)); 16 | if (i < 6) 17 | assertTrue(hole.pegFits(peg)); 18 | else 19 | assertFalse(hole.pegFits(peg)); 20 | } 21 | } 22 | // END java_square_peg_test 23 | } 24 | -------------------------------------------------------------------------------- /functional_thinking_examples/groovy/complexnums/ComplexNumber.groovy: -------------------------------------------------------------------------------- 1 | package complexnums 2 | 3 | class ComplexNumber { 4 | def real, imaginary 5 | 6 | public ComplexNumber(real, imaginary) { 7 | this.real = real 8 | this.imaginary = imaginary 9 | } 10 | 11 | def plus(rhs) { 12 | new ComplexNumber(this.real + rhs.real, this.imaginary + rhs.imaginary) 13 | } 14 | // formula: (x + yi)(u + vi) = (xu – yv) + (xv + yu)i. 15 | def multiply(rhs) { 16 | new ComplexNumber( 17 | real * rhs.real - imaginary * rhs.imaginary, 18 | real * rhs.imaginary + imaginary * rhs.real) 19 | } 20 | 21 | def String toString() { 22 | real.toString() + ((imaginary < 0 ? "" : "+") + imaginary + "i").toString() 23 | } 24 | } -------------------------------------------------------------------------------- /functional_thinking_examples/scala/complex_numbers/ComplexTest.scala: -------------------------------------------------------------------------------- 1 | import org.scalatest.FunSuite 2 | 3 | class ComplexTest extends FunSuite { 4 | 5 | def fixture = 6 | new { 7 | val a = new Complex(1, 2) 8 | val b = new Complex(30, 40) 9 | } 10 | 11 | test("plus") { 12 | val f = fixture 13 | val z = f.a + f.b 14 | assert(1 + 30 == z.real) 15 | } 16 | 17 | test("comparison") { 18 | val f = fixture 19 | assert(f.a < f.b) 20 | assert(new Complex(1, 2) >= new Complex(3, 4)) 21 | assert(new Complex(1, 1) < new Complex(2,2)) 22 | assert(new Complex(-10, -10) > new Complex(1, 1)) 23 | assert(new Complex(1, 2) >= new Complex(1, 2)) 24 | assert(new Complex(1, 2) <= new Complex(1, 2)) 25 | } 26 | 27 | } -------------------------------------------------------------------------------- /functional_thinking_examples/groovy/number_classifier/NumberClassifier.groovy: -------------------------------------------------------------------------------- 1 | package com.nealford.numberclassifier 2 | 3 | 4 | class NumberClassifier { 5 | 6 | // BEGIN groovy_filter 7 | static def factors(number) { 8 | (1..number).findAll {number % it == 0} 9 | } 10 | // END groovy_filter 11 | 12 | // BEGIN groovy_reduce 13 | static def sumFactors(number) { 14 | factors(number).inject(0, {i, j -> i + j}) 15 | } 16 | // END groovy_reduce 17 | 18 | static def isPerfect(number) { 19 | sumFactors(number) - number == number 20 | } 21 | 22 | static def isAbundant(number) { 23 | sumFactors(number) - number > number 24 | } 25 | 26 | static def isDeficient(number) { 27 | sumFactors(number) - number < number 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /functional_thinking_examples/java/trans/TheCompanyProcess.java: -------------------------------------------------------------------------------- 1 | package com.nealford.functionalthinking.trans; 2 | 3 | import java.util.List; 4 | 5 | public class TheCompanyProcess { 6 | public String cleanNames(List listOfNames) { 7 | StringBuilder result = new StringBuilder(); 8 | for(int i = 0; i < listOfNames.size(); i++) { 9 | if (listOfNames.get(i).length() > 1) { 10 | result.append(capitalizeString(listOfNames.get(i))).append(","); 11 | } 12 | } 13 | return result.substring(0, result.length() - 1).toString(); 14 | } 15 | 16 | public String capitalizeString(String s) { 17 | return s.substring(0, 1).toUpperCase() + s.substring(1, s.length()); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /functional_thinking_examples/groovy/easyb/scenario.groovy: -------------------------------------------------------------------------------- 1 | package org.easyb.bdd.specification.queue 2 | 3 | import org.easyb.bdd.Queue 4 | 5 | description "This is how a Queue must work" 6 | 7 | before "initialize the queue for each spec", { 8 | queue = new Queue() 9 | } 10 | 11 | it "should dequeue item just enqueued", { 12 | queue.enqueue(2) 13 | queue.dequeue().shouldBe(2) 14 | } 15 | 16 | it "should throw an exception when null is enqueued", { 17 | ensureThrows(RuntimeException.class) { 18 | queue.enqueue(null) 19 | } 20 | } 21 | 22 | it "should dequeue items in same order enqueued", { 23 | [1..5].each {val -> 24 | queue.enqueue(val) 25 | } 26 | [1..5].each {val -> 27 | queue.dequeue().shouldBe(val) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /functional_thinking_examples/java/composition/Factors.java: -------------------------------------------------------------------------------- 1 | package com.nealford.conf.ft.composition; 2 | 3 | import java.util.HashSet; 4 | import java.util.Set; 5 | 6 | import static java.lang.Math.sqrt; 7 | 8 | // BEGIN java_factors 9 | public class Factors { 10 | static public boolean isFactor(int number, int potential_factor) { 11 | return number % potential_factor == 0; 12 | } 13 | 14 | static public Set of(int number) { 15 | HashSet factors = new HashSet<>(); 16 | for (int i = 1; i <= sqrt(number); i++) 17 | if (isFactor(number, i)) { 18 | factors.add(i); 19 | factors.add(number / i); 20 | } 21 | return factors; 22 | } 23 | 24 | } 25 | // END java_factors 26 | -------------------------------------------------------------------------------- /functional_thinking_examples/groovy/allaboutlists/LazyList.groovy: -------------------------------------------------------------------------------- 1 | package com.nealford.ft.allaboutlists 2 | 3 | class LazyList { 4 | private head, tail 5 | 6 | LazyList(head, tail) { 7 | this.head = head; 8 | this.tail = tail 9 | } 10 | 11 | def LazyList getTail() { tail ? tail() : null } 12 | 13 | def List getHead(n) { 14 | def harvestedValues = []; 15 | def current = this 16 | n.times { 17 | harvestedValues << current.head 18 | current = current.tail 19 | } 20 | harvestedValues 21 | } 22 | 23 | def LazyList filter(Closure p) { 24 | if (p(head)) 25 | p.owner.prepend(head, { getTail().filter(p) }) 26 | else 27 | getTail().filter(p) 28 | } 29 | } -------------------------------------------------------------------------------- /functional_thinking_examples/1_02_step1.java: -------------------------------------------------------------------------------- 1 | //simplify corner cases 2 | public static int indexOfAny(String str, char[] searchChars) { 3 | when(searchChars) { <1> 4 | int csLen = str.length(); 5 | int csLast = csLen - 1; 6 | int searchLen = searchChars.length; 7 | int searchLast = searchLen - 1; 8 | for (int i = 0; i < csLen; i++) { 9 | char ch = str.charAt(i); 10 | for (int j = 0; j < searchLen; j++) { 11 | if (searchChars[j] == ch) { 12 | if (i < csLast && j < searchLast && CharUtils.isHighSurrogate(ch)) { 13 | if (searchChars[j + 1] == str.charAt(i + 1)) { 14 | return i; 15 | } 16 | } else { 17 | return i; 18 | } 19 | } 20 | } 21 | } 22 | return INDEX_NOT_FOUND; 23 | } 24 | } -------------------------------------------------------------------------------- /functional_thinking_examples/groovy/memoization/ClassifierMemoizedSum.groovy: -------------------------------------------------------------------------------- 1 | package com.nealford.ft.memoization 2 | 3 | class ClassifierMemoizedSum { 4 | def static isFactor(number, potential) { 5 | number % potential == 0; 6 | } 7 | 8 | def static factorsOf(number) { 9 | (1..number).findAll { i -> isFactor(number, i) } 10 | } 11 | 12 | def static sumFactors = { number -> 13 | factorsOf(number).inject(0, {i, j -> i + j}) 14 | } 15 | def static sumOfFactors = sumFactors.memoize() 16 | 17 | def static isPerfect(number) { 18 | sumOfFactors(number) == 2 * number 19 | } 20 | 21 | def static isAbundant(number) { 22 | sumOfFactors(number) > 2 * number 23 | } 24 | 25 | def static isDeficient(number) { 26 | sumOfFactors(number) < 2 * number 27 | } 28 | } 29 | 30 | 31 | -------------------------------------------------------------------------------- /functional_thinking_examples/scala/pattern_matching/Colors.scala: -------------------------------------------------------------------------------- 1 | // BEGIN scala_colors 2 | class Color(val red:Int, val green:Int, val blue:Int) 3 | 4 | case class Red(r:Int) extends Color(r, 0, 0) 5 | case class Green(g:Int) extends Color(0, g, 0) 6 | case class Blue(b:Int) extends Color(0, 0, b) 7 | 8 | def printColor(c:Color) = c match { 9 | case Red(v) => println("Red: " + v) 10 | case Green(v) => println("Green: " + v) 11 | case Blue(v) => println("Blue: " + v) 12 | case col:Color => { 13 | print("R: " + col.red + ", ") 14 | print("G: " + col.green + ", ") 15 | println("B: " + col.blue) 16 | } 17 | 18 | case null => println("invalid color") 19 | } 20 | // END scala_colors 21 | 22 | printColor(new Color(34, 33, 34)) 23 | printColor(new Color(100, 0, 0)) 24 | printColor(Red(255)) 25 | printColor(null) 26 | -------------------------------------------------------------------------------- /functional_thinking_examples/groovy/memoization/ClassifierMemoized.groovy: -------------------------------------------------------------------------------- 1 | package com.nealford.ft.memoization 2 | 3 | class ClassifierMemoized { 4 | def static dividesBy = { number, potential -> 5 | number % potential == 0 6 | } 7 | def static isFactor = dividesBy.memoize() 8 | 9 | def static factorsOf(number) { 10 | (1..number).findAll { i -> isFactor.call(number, i) } 11 | } 12 | 13 | def static sumFactors = { number -> 14 | factorsOf(number).inject(0, {i, j -> i + j}) 15 | } 16 | def static sumOfFactors = sumFactors.memoize() 17 | 18 | def static isPerfect(number) { 19 | sumOfFactors(number) == 2 * number 20 | } 21 | 22 | def static isAbundant(number) { 23 | sumOfFactors(number) > 2 * number 24 | } 25 | 26 | def static isDeficient(number) { 27 | sumOfFactors(number) < 2 * number 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /functional_thinking_examples/groovy/easyb/behaviorcategory.groovy: -------------------------------------------------------------------------------- 1 | static void shouldBe(Object self, value, String msg) { 2 | isEqual(self, value, msg) 3 | } 4 | 5 | private static void isEqual(self, value, String msg) { 6 | if (self.getClass() == NullObject.class) { 7 | if (value != null) { 8 | throwValidationException( 9 | "expected ${value.toString()} but target object is null", msg) 10 | } 11 | } else if (value.getClass() == String.class) { 12 | if (!value.toString().equals(self.toString())) { 13 | throwValidationException( 14 | "expected ${value.toString()} but was ${self.toString()}", msg) 15 | } 16 | } else { 17 | if (value != self) { 18 | throwValidationException("expected ${value} but was ${self}", msg) 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /functional_thinking_examples/groovy/memoization/ClassifierCached.groovy: -------------------------------------------------------------------------------- 1 | class ClassifierCached { 2 | private sumCache = [:], factorCache = [:] 3 | 4 | def sumOfFactors(number) { 5 | if (! sumCache.containsKey(number)) 6 | sumCache[number] = factorsOf(number).sum() 7 | sumCache[number] 8 | } 9 | 10 | def isFactor(number, potential) { 11 | number % potential == 0; 12 | } 13 | 14 | def factorsOf(number) { 15 | if (! factorCache.containsKey(number)) 16 | factorCache[number] = (1..number).findAll {isFactor(number, it)} 17 | factorCache[number] 18 | } 19 | 20 | def isPerfect(number) { 21 | sumOfFactors(number) == 2 * number 22 | } 23 | 24 | def isAbundant(number) { 25 | sumOfFactors(number) > 2 * number 26 | } 27 | 28 | def isDeficient(number) { 29 | sumOfFactors(number) < 2 * number 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /functional_thinking_examples/java/composition/FClassifier.java: -------------------------------------------------------------------------------- 1 | package com.nealford.conf.ft.composition; 2 | 3 | import java.util.Iterator; 4 | 5 | // BEGIN java_fclassifier 6 | public class FClassifier { 7 | 8 | public static int sumOfFactors(int number) { 9 | Iterator it = Factors.of(number).iterator(); 10 | int sum = 0; 11 | while (it.hasNext()) 12 | sum += it.next(); 13 | return sum; 14 | } 15 | 16 | public static boolean isPerfect(int number) { 17 | return sumOfFactors(number) - number == number; 18 | } 19 | 20 | public static boolean isAbundant(int number) { 21 | return sumOfFactors(number) - number > number; 22 | } 23 | 24 | public static boolean isDeficient(int number) { 25 | return sumOfFactors(number) - number < number; 26 | } 27 | } 28 | // END java_fclassifier 29 | -------------------------------------------------------------------------------- /functional_thinking_examples/java/composition/FactorsBeta.java: -------------------------------------------------------------------------------- 1 | package com.nealford.conf.ft.composition; 2 | 3 | import java.util.Set; 4 | import static java.lang.Math.sqrt; 5 | import java.util.HashSet; 6 | 7 | // BEGIN java_factors_beta 8 | public class FactorsBeta { 9 | protected int number; 10 | 11 | public FactorsBeta(int number) { 12 | this.number = number; 13 | } 14 | 15 | public boolean isFactor(int potential_factor) { 16 | return number % potential_factor == 0; 17 | } 18 | 19 | public Set getFactors() { 20 | HashSet factors = new HashSet<>(); 21 | for (int i = 1; i <= sqrt(number); i++) 22 | if (isFactor(i)) { 23 | factors.add(i); 24 | factors.add(number / i); 25 | } 26 | return factors; 27 | } 28 | } 29 | // END java_factors_beta 30 | -------------------------------------------------------------------------------- /functional_thinking_examples/groovy/allaboutlists/NumberClassifier.groovy: -------------------------------------------------------------------------------- 1 | package com.nealford.ft.allaboutlists 2 | 3 | import static com.nealford.ft.allaboutlists.NumberClassification.* 4 | 5 | def enum NumberClassification { 6 | PERFECT, ABUNDANT, DEFICIENT 7 | } 8 | 9 | class NumberClassifier { 10 | static def factorsOf(number) { 11 | (1..number).findAll { i -> number % i == 0 } 12 | } 13 | 14 | static def classify(number) { 15 | switch (factorsOf(number).inject(0, { i, j -> i + j })) { 16 | case { it < 2 * number }: return DEFICIENT 17 | case { it > 2 * number }: return ABUNDANT 18 | case { it == 2 * number }: return PERFECT 19 | } 20 | } 21 | 22 | static def isPerfect(number) { 23 | classify(number) == PERFECT 24 | } 25 | 26 | 27 | static def nextPerfectNumberAfter(n) { 28 | while (!isPerfect(++n)); 29 | n 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /functional_thinking_examples/groovy/patterns_adaptor/pegs.groovy: -------------------------------------------------------------------------------- 1 | package pegs; 2 | 3 | // BEGIN groovy_pegs 4 | class SquarePeg { 5 | def width 6 | } 7 | 8 | class RoundPeg { 9 | def radius 10 | } 11 | 12 | class RoundHole { 13 | def radius 14 | 15 | def pegFits(peg) { 16 | peg.radius <= radius 17 | } 18 | 19 | String toString() { "RoundHole with radius $radius" } 20 | } 21 | 22 | class SquarePegAdapter { 23 | def peg 24 | 25 | def getRadius() { 26 | Math.sqrt(((peg.width/2) ** 2)*2) 27 | } 28 | 29 | String toString() { 30 | "SquarePegAdapter with peg width $peg.width "+ 31 | "(and notional radius $radius)" 32 | } 33 | } 34 | // END groovy_pegs 35 | 36 | def roundPegOf(squarePeg) { 37 | { p -> [getRadius:{Math.sqrt( 38 | ((p.width/2) ** 2)*2)}] as RoundThing 39 | } 40 | } 41 | 42 | 43 | -------------------------------------------------------------------------------- /functional_thinking_examples/scala/trans/Process.scala: -------------------------------------------------------------------------------- 1 | import scala.compat.Platform; 2 | 3 | // BEGIN scala_process 4 | val employees = List("neal", "s", "stu", "j", "rich", "bob", "aiden", "j", "ethan", 5 | "liam", "mason", "noah", "lucas", "jacob", "jayden", "jack") 6 | 7 | val result = employees 8 | .filter(_.length() > 1) 9 | .map(_.capitalize) 10 | .reduce(_ + "," + _) 11 | // END scala_process 12 | println("Process:" + result) 13 | 14 | // BEGIN scala_process_parallel 15 | val parallelResult = employees 16 | .par 17 | .filter(_.length() > 1) 18 | .map(_.capitalize) 19 | .reduce(_ + "," + _) 20 | // END scala_process_parallel 21 | println("Parallel:" + parallelResult) 22 | 23 | println("\nBoundary:\n================") 24 | val bresult = List("") 25 | .filter(_.length() > 1) 26 | .map(_.capitalize) 27 | 28 | println("Boundary:" + bresult) 29 | 30 | 31 | -------------------------------------------------------------------------------- /functional_thinking_examples/groovy/number_classifier/NumberClassifierOpt.groovy: -------------------------------------------------------------------------------- 1 | package com.nealford.numberclassifier 2 | 3 | import static java.lang.Math.round 4 | import static java.lang.Math.sqrt 5 | 6 | 7 | class NumberClassifierOpt { 8 | 9 | // BEGIN groovy_mapping 10 | static def factors(number) { 11 | def factors = (1..round(sqrt(number)+1)).findAll({number % it == 0}) 12 | (factors + factors.collect {number / it}).unique() 13 | } 14 | // END groovy_mapping 15 | 16 | static def sumFactors(number) { 17 | factors(number).inject(0, {i, j -> i + j}) 18 | } 19 | 20 | static def isPerfect(number) { 21 | sumFactors(number) - number == number 22 | } 23 | 24 | static def isAbundant(number) { 25 | sumFactors(number) - number > number 26 | } 27 | 28 | static def isDeficient(number) { 29 | sumFactors(number) - number < number 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /functional_thinking_examples/groovy/camelize_category/CamelizeCategoryTest.groovy: -------------------------------------------------------------------------------- 1 | package com.nealford.functionalthinking 2 | 3 | import org.junit.Test 4 | 5 | import static groovy.util.GroovyTestCase.assertEquals 6 | 7 | class CamelizeCategoryTest { 8 | // BEGIN groovy_camelize_category_test 9 | @Test void testCamelize() { 10 | def expected = [event_map: "eventMap", 11 | name: "name", test_date: "testDate", 12 | test_string_with_breaks: "testStringWithBreaks"] 13 | use(CamelizeCategory) { 14 | expected.each { key, value -> 15 | assertEquals value, key.camelize() 16 | } 17 | } 18 | } 19 | // END groovy_camelize_category_test 20 | 21 | 22 | @Test(expected = groovy.lang.MissingMethodException) 23 | void testCannotCamelizeNonString() { 24 | int i = 12 25 | use(CamelizeCategory) { 26 | i.camelize() 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /functional_thinking_examples/clojure/color_dispatch/core.clj: -------------------------------------------------------------------------------- 1 | 2 | (ns color-dispatch.core) 3 | 4 | ;BEGIN clj_color 5 | (defstruct color :red :green :blue) 6 | 7 | (defn red [v] 8 | (struct color v 0 0)) 9 | 10 | (defn green [v] 11 | (struct color 0 v 0)) 12 | 13 | (defn blue [v] 14 | (struct color 0 0 v)) 15 | ;END clj_color 16 | 17 | ;BEGIN clj_colors_in 18 | (defn basic-colors-in [color] 19 | (for [[k v] color :when (not= v 0)] k)) 20 | 21 | (defmulti color-string basic-colors-in) 22 | 23 | (defmethod color-string [:red] [color] 24 | (str "Red: " (:red color))) 25 | 26 | (defmethod color-string [:green] [color] 27 | (str "Green: " (:green color))) 28 | 29 | (defmethod color-string [:blue] [color] 30 | (str "Blue: " (:blue color))) 31 | 32 | (defmethod color-string :default [color] 33 | (str "Red:" (:red color) ", Green: " (:green color) ", Blue: " (:blue color))) 34 | 35 | ;END clj_colors_in -------------------------------------------------------------------------------- /functional_thinking_examples/groovy/complexnums/ComplexNumberTest.groovy: -------------------------------------------------------------------------------- 1 | package complexnums 2 | 3 | import org.junit.Test 4 | import static org.junit.Assert.assertTrue 5 | import org.junit.Before 6 | 7 | class ComplexNumberTest { 8 | def x, y 9 | 10 | @Before void setup() { 11 | x = new ComplexNumber(3, 2) 12 | y = new ComplexNumber(1, 4) 13 | } 14 | 15 | @Test void plus() { 16 | def z = x + y; 17 | assertTrue 3 + 1 == z.real 18 | assertTrue 2 + 4 == z.imaginary 19 | } 20 | 21 | @Test void multiply() { 22 | def z = x * y 23 | assertTrue(-5 == z.real) 24 | assertTrue 14 == z.imaginary 25 | } 26 | 27 | @Test void to_string() { 28 | assertTrue "3+2i" == x.toString() 29 | assertTrue "4+6i" == (x + y).toString() 30 | assertTrue "3+0i" == new ComplexNumber(3, 0).toString() 31 | assertTrue "4-2i" == new ComplexNumber(4, -2).toString() 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /functional_thinking_examples/java/composition/ClassifierBeta.java: -------------------------------------------------------------------------------- 1 | package com.nealford.conf.ft.composition; 2 | 3 | import java.util.Iterator; 4 | import java.util.Set; 5 | 6 | // BEGIN java_classifier_beta 7 | public class ClassifierBeta extends FactorsBeta { 8 | 9 | public ClassifierBeta(int number) { 10 | super(number); 11 | } 12 | 13 | public int sum() { 14 | Iterator it = getFactors().iterator(); 15 | int sum = 0; 16 | while (it.hasNext()) 17 | sum += (Integer) it.next(); 18 | return sum; 19 | } 20 | 21 | public boolean isPerfect() { 22 | return sum() - number == number; 23 | } 24 | 25 | public boolean isAbundant() { 26 | return sum() - number > number; 27 | } 28 | 29 | public boolean isDeficient() { 30 | return sum() - number < number; 31 | } 32 | 33 | } 34 | // END java_classifier_beta 35 | -------------------------------------------------------------------------------- /functional_thinking_examples/groovy/open_classes/test_string_open_class.groovy: -------------------------------------------------------------------------------- 1 | import groovy.util.GroovyTestCase 2 | 3 | // BEGIN groovy_camelize 4 | class TestStringCategory extends GroovyTestCase { 5 | static { 6 | String.metaClass.camelize = { -> 7 | def newName = delegate.split("_").collect() { 8 | it.substring(0, 1).toUpperCase() + 9 | it.substring(1, it.length()) 10 | }.join() 11 | newName.substring(0, 1).toLowerCase() + 12 | newName.substring(1, newName.length()) 13 | } 14 | } 15 | 16 | def expected = [event_map : "eventMap", 17 | name : "name", test_date : "testDate", 18 | test_string_with_breaks : "testStringWithBreaks" ] 19 | 20 | void test_Camelize() { 21 | expected.each { key, value -> 22 | assertEquals value, key.camelize() 23 | } 24 | } 25 | } 26 | // END groovy_camelize 27 | -------------------------------------------------------------------------------- /functional_thinking_examples/scala/letter_grades/LetterGrade.scala: -------------------------------------------------------------------------------- 1 | // BEGIN scala_letter_grades 2 | val VALID_GRADES = Set("A", "B", "C", "D", "F") 3 | 4 | def letterGrade(value: Any) : String = value match { 5 | case x:Int if (90 to 100).contains(x) => "A" 6 | case x:Int if (80 to 90).contains(x) => "B" 7 | case x:Int if (70 to 80).contains(x) => "C" 8 | case x:Int if (60 to 70).contains(x) => "D" 9 | case x:Int if (0 to 60).contains(x) => "F" 10 | case x:String if VALID_GRADES(x.toUpperCase) => x.toUpperCase 11 | } 12 | // END scala_letter_grades 13 | 14 | // BEGIN scala_letter_grade_test 15 | printf("Amy scores %d and receives %s\n", 91, letterGrade(91)) 16 | printf("Bob scores %d and receives %s\n", 72, letterGrade(72)) 17 | printf("Sam never attened class, scored %d, and received %s\n", 18 | 44, letterGrade(44)) 19 | printf("Roy transfered and already had %s, which translated as %s\n", 20 | "B", letterGrade("B")) 21 | // END scala_letter_grade_test 22 | -------------------------------------------------------------------------------- /functional_thinking_examples/1_01_indexOfAny.java: -------------------------------------------------------------------------------- 1 | // From Apache Commons Lang, http://commons.apache.org/lang/ 2 | public static int indexOfAny(String str, char[] searchChars) { 3 | if (isEmpty(str) || ArrayUtils.isEmpty(searchChars)) { <1> 4 | return INDEX_NOT_FOUND; 5 | } 6 | int csLen = str.length(); <2> 7 | int csLast = csLen - 1; 8 | int searchLen = searchChars.length; 9 | int searchLast = searchLen - 1; 10 | for (int i = 0; i < csLen; i++) { <3> 11 | char ch = str.charAt(i); 12 | for (int j = 0; j < searchLen; j++) { <4> 13 | if (searchChars[j] == ch) { <5> 14 | if (i < csLast && j < searchLast && CharUtils.isHighSurrogate(ch)) { 15 | if (searchChars[j + 1] == str.charAt(i + 1)) { 16 | return i; 17 | } 18 | } else { 19 | return i; 20 | } 21 | } 22 | } 23 | } 24 | return INDEX_NOT_FOUND; 25 | } -------------------------------------------------------------------------------- /functional_thinking_examples/java/errorhandling/Either.java: -------------------------------------------------------------------------------- 1 | package com.nealford.ft.errorhandling; 2 | 3 | public class Either { 4 | private A left = null; 5 | private B right = null; 6 | 7 | private Either(A a,B b) { 8 | left = a; 9 | right = b; 10 | } 11 | 12 | public static Either left(A a) { 13 | return new Either(a,null); 14 | } 15 | 16 | public A left() { 17 | return left; 18 | } 19 | 20 | public boolean isLeft() { 21 | return left != null; 22 | } 23 | 24 | public boolean isRight() { 25 | return right != null; 26 | } 27 | 28 | public B right() { 29 | return right; 30 | } 31 | 32 | public static Either right(B b) { 33 | return new Either(null,b); 34 | } 35 | 36 | public void fold(F leftOption, F rightOption) { 37 | if(right == null) 38 | leftOption.f(left); 39 | else 40 | rightOption.f(right); 41 | } 42 | } -------------------------------------------------------------------------------- /functional_thinking_examples/groovy/memoization/ClassifierCachedSum.groovy: -------------------------------------------------------------------------------- 1 | package com.nealford.ft.memoization 2 | 3 | 4 | // Things to note: 5 | // 1. can't do static now because of ripple effect from sumOfFactors 6 | // 7 | // BEGIN groovy_classifier_cached_sum 8 | class ClassifierCachedSum { 9 | private sumCache = [:] 10 | 11 | def sumOfFactors(number) { 12 | if (! sumCache.containsKey(number)) { 13 | sumCache[number] = factorsOf(number).sum() 14 | } 15 | return sumCache[number] 16 | 17 | } 18 | // remainder of code unchanged... 19 | // END groovy_classifier_cached_sum 20 | 21 | def isFactor(number, potential) { 22 | number % potential == 0; 23 | } 24 | 25 | def factorsOf(number) { 26 | (1..number).findAll { i -> isFactor(number, i) } 27 | } 28 | 29 | def isPerfect(number) { 30 | sumOfFactors(number) == 2 * number 31 | } 32 | 33 | def isAbundant(number) { 34 | sumOfFactors(number) > 2 * number 35 | } 36 | 37 | def isDeficient(number) { 38 | sumOfFactors(number) < 2 * number 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /functional_thinking_examples/groovy/camelize_expando/TestCamelizeExpando.groovy: -------------------------------------------------------------------------------- 1 | package com.nealford.functionalthinking 2 | 3 | import org.junit.Test 4 | 5 | import static groovy.util.GroovyTestCase.assertEquals 6 | 7 | /** 8 | * (probably) Copyright 2013 by Neal Ford. All rights reserved. 9 | */ 10 | class TestCamelizeExpando { 11 | 12 | // BEGIN groovy_camelize_expando 13 | static { 14 | String.metaClass.camelize = {-> 15 | def newName = delegate.split("_").collect() { 16 | it.substring(0, 1).toUpperCase() + 17 | it.substring(1, it.length()) 18 | }.join() 19 | newName.substring(0, 1).toLowerCase() + 20 | newName.substring(1, newName.length()) 21 | } 22 | } 23 | 24 | def expected = [event_map: "eventMap", 25 | name: "name", test_date: "testDate", 26 | test_string_with_breaks: "testStringWithBreaks"] 27 | 28 | @Test void can_camelize() { 29 | expected.each { key, value -> 30 | assertEquals value, key.camelize() 31 | } 32 | } 33 | // END groovy_camelize_expando 34 | } 35 | -------------------------------------------------------------------------------- /functional_thinking_examples/java/transformations_functionaljava/PrimeNumberClassifier.java: -------------------------------------------------------------------------------- 1 | package com.nealford.ft.imperativeprimes; 2 | 3 | import java.util.HashSet; 4 | import java.util.Set; 5 | 6 | public class PrimeNumberClassifier { 7 | private Integer number; 8 | 9 | public PrimeNumberClassifier(int number) { 10 | this.number = number; 11 | } 12 | 13 | public boolean isFactor(int potential) { 14 | return number % potential == 0; 15 | } 16 | 17 | public Set getFactors() { 18 | Set factors = new HashSet(); 19 | factors.add(1); 20 | factors.add(number); 21 | for (Integer i = 2; i < number; i++) 22 | if (isFactor(i)) 23 | factors.add(i); 24 | return factors; 25 | } 26 | 27 | public int sumFactors() { 28 | int sum = 0; 29 | for (int i : getFactors()) 30 | sum += i; 31 | return sum; 32 | } 33 | 34 | public boolean isPrime() { 35 | return sumFactors() == number + 1; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /functional_thinking_examples/groovy/polydispatch/ProductTest.groovy: -------------------------------------------------------------------------------- 1 | import org.junit.Test 2 | 3 | import com.nealford.ft.polydispatch.Multiply 4 | 5 | import static org.junit.Assert.assertEquals 6 | import com.nealford.ft.polydispatch.Incrementation 7 | 8 | import static org.junit.Assert.assertTrue 9 | 10 | import com.nealford.ft.polydispatch.ProductFactory; 11 | 12 | class ProductTest { 13 | @Test 14 | public void mutiplyTest() { 15 | def p = new Multiply() 16 | assertEquals(20, p.evaluate(5, 4)); 17 | } 18 | 19 | @Test 20 | public void incrementTest() { 21 | def p = new Incrementation() 22 | assertEquals(20, p.evaluate(4, 5)) 23 | } 24 | 25 | // BEGIN groovy_decisionTest 26 | @Test 27 | public void decisionTest() { 28 | def p = ProductFactory.getProduct(10010) 29 | assertTrue p.getClass() == Multiply.class 30 | assertEquals(2*10010, p.evaluate(2, 10010)) 31 | p = ProductFactory.getProduct(9000) 32 | assertTrue p.getClass() == Incrementation.class 33 | assertEquals(3*3000, p.evaluate(3, 3000)) 34 | } 35 | // END groovy_decisionTest 36 | } 37 | -------------------------------------------------------------------------------- /functional_thinking_examples/groovy/patterns_adaptor/pegs_test.groovy: -------------------------------------------------------------------------------- 1 | import org.junit.Test 2 | import org.junit.runner.JUnitCore 3 | import static org.junit.Assert.* 4 | import static junit.framework.Assert.*; 5 | import static org.hamcrest.CoreMatchers.* 6 | import static org.junit.matchers.JUnitMatchers.* 7 | import pegs.*; 8 | 9 | class PegsTest { 10 | // BEGIN groovy_pegs_test 11 | @Test void pegs_and_holes() { 12 | def hole = new RoundHole(radius:4.0) 13 | (4..7).each { w -> 14 | def peg = new SquarePegAdapter(peg:new SquarePeg(width:w)) 15 | if (w < 6 ) 16 | assertTrue hole.pegFits(peg) 17 | else 18 | assertFalse hole.pegFits(peg) 19 | } 20 | } 21 | // END groovy_pegs_test 22 | 23 | @Test void pegs_with_inherited_adaptor() { 24 | def hole = new RoundHole(radius:4.0) 25 | (4..7).each { w -> 26 | def peg = new SquarePegAdapterUsingInheritance(width:w) 27 | if (w < 6) 28 | assertTrue hole.pegFits(peg) 29 | else 30 | assertFalse hole.pegFits(peg) 31 | } 32 | } 33 | } 34 | 35 | JUnitCore.main('PegsTest') 36 | -------------------------------------------------------------------------------- /functional_thinking_examples/java/composition/PrimeAlpha.java: -------------------------------------------------------------------------------- 1 | package com.nealford.conf.ft.composition; 2 | 3 | import com.sun.tools.javac.util.List; 4 | 5 | import java.util.HashSet; 6 | import java.util.Set; 7 | 8 | import static java.lang.Math.sqrt; 9 | 10 | // BEGIN java_prime_alpha 11 | public class PrimeAlpha { 12 | private int number; 13 | 14 | public PrimeAlpha(int number) { 15 | this.number = number; 16 | } 17 | 18 | public boolean isPrime() { 19 | Set primeSet = new HashSet() {{ 20 | add(1); add(number);}}; 21 | return number > 1 && 22 | factors().equals(primeSet); 23 | } 24 | 25 | public boolean isFactor(int potential_factor) { 26 | return number % potential_factor == 0; 27 | } 28 | 29 | public Set factors() { 30 | HashSet factors = new HashSet<>(); 31 | for (int i = 1; i <= sqrt(number); i++) 32 | if (isFactor(i)) { 33 | factors.add(i); 34 | factors.add(number / i); 35 | } 36 | return factors; 37 | } 38 | 39 | } 40 | // END java_prime_alpha 41 | -------------------------------------------------------------------------------- /functional_thinking_examples/java/trans8/Process.java: -------------------------------------------------------------------------------- 1 | package com.nealford.funtionalthinking.trans; 2 | 3 | import java.util.List; 4 | import java.util.Optional; 5 | import java.util.stream.Collectors; 6 | 7 | public class Process { 8 | 9 | // BEGIN java8_process 10 | public String cleanNames(List names) { 11 | if (names == null) return ""; 12 | return names 13 | .stream() 14 | .filter(name -> name.length() > 1) 15 | .map(name -> capitalize(name)) 16 | .collect(Collectors.joining(",")); 17 | } 18 | 19 | private String capitalize(String e) { 20 | return e.substring(0, 1).toUpperCase() + e.substring(1, e.length()); 21 | } 22 | // END java8_process 23 | 24 | 25 | // BEGIN java8_process_parallel 26 | public String cleanNamesP(List names) { 27 | if (names == null) return ""; 28 | return names 29 | .parallelStream() 30 | .filter(n -> n.length() > 1) 31 | .map(e -> capitalize(e)) 32 | .collect(Collectors.joining(",")); 33 | } 34 | // END java8_process_parallel 35 | 36 | } 37 | -------------------------------------------------------------------------------- /functional_thinking_examples/groovy/simple_closure/SimpleClosure.groovy: -------------------------------------------------------------------------------- 1 | // BEGIN simple_closure_intro 2 | class Employee { 3 | def name, salary 4 | } 5 | 6 | def paidMore(amount) { 7 | return {Employee e -> e.salary > amount} 8 | } 9 | 10 | isHighPaid = paidMore(100000) 11 | // END simple_closure_intro 12 | 13 | // BEGIN simple_closure_demo 14 | def Smithers = new Employee(name:"Fred", salary:120000) 15 | def Homer = new Employee(name:"Homer", salary:80000) 16 | println isHighPaid(Smithers) 17 | println isHighPaid(Homer) 18 | // true, false 19 | // END simple_closure_demo 20 | 21 | // BEGIN simple_closure_addition 22 | isHigherPaid = paidMore(200000) 23 | println isHigherPaid(Smithers) 24 | println isHigherPaid(Homer) 25 | def Burns = new Employee(name:"Monty", salary:1000000) 26 | println isHigherPaid(Burns) 27 | // false, false, true 28 | // END simple_closure_addition 29 | 30 | 31 | def paidCorrectly() { 32 | return {emp, policy -> emp.salary <= policy.maxSalary} 33 | } 34 | 35 | class Policy { 36 | def maxSalary 37 | } 38 | 39 | def review = paidCorrectly() 40 | def p = new Policy(maxSalary: 90_000) 41 | println("policy\n================") 42 | println review(Smithers, p) 43 | -------------------------------------------------------------------------------- /functional_thinking_examples/java/wordfreq/Words.java: -------------------------------------------------------------------------------- 1 | package com.nealford.functionalthinking.wordfreq; 2 | 3 | import java.util.*; 4 | import java.util.regex.Matcher; 5 | import java.util.regex.Pattern; 6 | 7 | // BEGIN java_word_freq 8 | public class Words { 9 | private Set NON_WORDS = new HashSet() {{ 10 | add("the"); add("and"); add("of"); add("to"); add("a"); 11 | add("i"); add("it"); add("in"); add("or"); add("is"); 12 | add("d"); add("s"); add("as"); add("so"); add("but"); 13 | add("be"); 14 | }}; 15 | 16 | public Map wordFreq(String words) { 17 | TreeMap wordMap = new TreeMap(); 18 | Matcher m = Pattern.compile("\\w+").matcher(words); 19 | while (m.find()) { 20 | String word = m.group().toLowerCase(); 21 | if (! NON_WORDS.contains(word)) { 22 | if (wordMap.get(word) == null) { 23 | wordMap.put(word, 1); 24 | } 25 | else { 26 | wordMap.put(word, wordMap.get(word) + 1); 27 | } 28 | } 29 | } 30 | return wordMap; 31 | } 32 | } 33 | // END java_word_freq 34 | -------------------------------------------------------------------------------- /functional_thinking_examples/groovy/iterate_recurse.groovy: -------------------------------------------------------------------------------- 1 | // BEGIN iterate_groovy 2 | def perfectNumbers = [6, 28, 496, 8128] 3 | 4 | def iterateList(listOfNums) { 5 | listOfNums.each { println "${it}" } 6 | } 7 | 8 | iterateList(perfectNumbers) 9 | // END iterate_groovy 10 | 11 | // BEGIN recurse_groovy 12 | def recurseList(listOfNums) { 13 | if (listOfNums.size > 0) { 14 | println "${listOfNums.head()}" 15 | recurseList(listOfNums.tail()) 16 | } 17 | } 18 | // END recurse_groovy 19 | 20 | recurseList(perfectNumbers) 21 | 22 | // BEGIN iterative_filter_groovy 23 | def filter(list, predicate) { 24 | def new_list = [] 25 | list.each { 26 | if (predicate(it)) { 27 | new_list << it 28 | } 29 | } 30 | return new_list 31 | } 32 | 33 | modBy2 = { n -> n % 2 == 0} 34 | 35 | l = filter(1..20, modBy2) 36 | println l 37 | // END iterative_filter_groovy 38 | 39 | // BEGIN recursive_filter_groovy 40 | def filterR(list, pred) { 41 | if (list.size() == 0) return list 42 | if (pred(list.head())) 43 | [] + list.head() + filterR(list.tail(), pred) 44 | else filterR(list.tail(), pred) 45 | } 46 | // END recursive_filter_groovy 47 | 48 | println "filtering" 49 | l = filter(1..20, {n-> n % 2 == 0}) 50 | l.each {i-> println "${i}"} 51 | 52 | -------------------------------------------------------------------------------- /functional_thinking_examples/groovy/currying.groovy: -------------------------------------------------------------------------------- 1 | // BEGIN groovy_adder_factory 2 | def adder = { x, y -> x + y} 3 | def incrementer = adder.curry(1) 4 | 5 | println "increment 7: ${incrementer(7)}" 6 | // END groovy_adder_factory 7 | 8 | // BEGIN groovy_currying 9 | def product = { x, y -> x * y } 10 | 11 | def quadrate = product.curry(4) // <1> 12 | def octate = product.curry(8) // <2> 13 | 14 | println "4x4: ${quadrate.call(4)}" // <3> 15 | println "8x5: ${octate(5)}" // <4> 16 | // END groovy_currying 17 | 18 | // BEGIN currying_partial 19 | def volume = {h, w, l -> h * w * l} 20 | def area = volume.curry(1) 21 | def lengthPA = volume.curry(1, 1) // <1> 22 | def lengthC = volume.curry(1).curry(1) // <2> 23 | 24 | println "The volume of the 2x3x4 rectangular solid is ${volume(2, 3, 4)}" 25 | println "The area of the 3x4 rectangle is ${area(3, 4)}" 26 | println "The length of the 6 line is ${lengthPA(6)}" 27 | println "The length of the 6 line via curried function is ${lengthC(6)}" 28 | // END currying_partial 29 | 30 | 31 | // BEGIN groovy_composite 32 | def composite = { f, g, x -> return f(g(x)) } 33 | def thirtyTwoer = composite.curry(quadrate, octate) 34 | 35 | println "composition of curried functions yields ${thirtyTwoer(2)}" 36 | // END groovy_composite 37 | -------------------------------------------------------------------------------- /functional_thinking_examples/java/oft_primes/Prime.java: -------------------------------------------------------------------------------- 1 | package com.nealford.functionalthinking.primes; 2 | 3 | import java.util.HashSet; 4 | import java.util.Set; 5 | 6 | import static java.lang.Math.sqrt; 7 | 8 | public class Prime { 9 | 10 | public static boolean isFactor(final int potential, final int number) { 11 | return number % potential == 0; 12 | } 13 | 14 | public static Set getFactors(final int number) { 15 | Set factors = new HashSet<>(); 16 | factors.add(1); 17 | factors.add(number); 18 | for (int i = 2; i < sqrt(number) + 1; i++) 19 | if (isFactor(i, number)) { 20 | factors.add(i); 21 | factors.add(number / i); 22 | } 23 | return factors; 24 | } 25 | 26 | public static int sumFactors(final int number) { 27 | int sum = 0; 28 | for (int i : getFactors(number)) 29 | sum += i; 30 | return sum; 31 | } 32 | 33 | public static boolean isPrime(final int number) { 34 | return sumFactors(number) == number + 1; 35 | } 36 | 37 | public static Integer nextPrimeFrom(final int lastPrime) { 38 | int candidate = lastPrime + 1; 39 | while (!isPrime(candidate)) candidate++; 40 | return candidate; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /functional_thinking_examples/java/structuralpatternmatching/Tree.java: -------------------------------------------------------------------------------- 1 | package com.nealford.ft.structuralpatternmatching; 2 | 3 | import fj.data.Either; 4 | import static fj.data.Either.left; 5 | import static fj.data.Either.right; 6 | 7 | public abstract class Tree { 8 | private Tree() {} 9 | 10 | public abstract Either> toEither(); 11 | 12 | public static final class Empty extends Tree { 13 | public Either> toEither() { 14 | return left(this); 15 | } 16 | 17 | public Empty() {} 18 | } 19 | 20 | public static final class Leaf extends Tree { 21 | public final int n; 22 | 23 | @Override 24 | public Either> toEither() { 25 | return right(Either.left(this)); 26 | } 27 | 28 | public Leaf(int n) { this.n = n; } 29 | } 30 | 31 | public static final class Node extends Tree { 32 | public final Tree left; 33 | public final Tree right; 34 | 35 | public Either> toEither() { 36 | return right(Either.right(this)); 37 | } 38 | 39 | public Node(Tree left, Tree right) { 40 | this.left = left; 41 | this.right = right; 42 | } 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /functional_thinking_examples/groovy/strategy/StrategyTest.groovy: -------------------------------------------------------------------------------- 1 | package com.nealford.ft.patterns 2 | 3 | import org.junit.Test 4 | import static groovy.util.GroovyTestCase.assertEquals 5 | 6 | // BEGIN groovy_calc 7 | interface Calc { 8 | def product(n, m) 9 | } 10 | 11 | class CalcMult implements Calc { 12 | def product(n, m) { n * m } 13 | } 14 | 15 | class CalcAdds implements Calc { 16 | def product(n, m) { 17 | def result = 0 18 | n.times { 19 | result += m 20 | } 21 | result 22 | } 23 | } 24 | // END groovy_calc 25 | 26 | // BEGIN groovy_strategy_test 27 | class StrategyTest { 28 | def listOfStrategies = [new CalcMult(), new CalcAdds()] 29 | 30 | @Test 31 | public void product_verifier() { 32 | listOfStrategies.each { s -> 33 | assertEquals(10, s.product(5, 2)) 34 | } 35 | } 36 | // END groovy_strategy_test 37 | 38 | // BEGIN groovy_functional_strategy 39 | @Test 40 | public void exp_verifier() { 41 | def listOfExp = [ 42 | {i, j -> Math.pow(i, j)}, 43 | {i, j -> 44 | def result = i 45 | (j-1).times { result *= i } 46 | result 47 | }] 48 | 49 | listOfExp.each { e -> 50 | assertEquals(32, e(2, 5)) 51 | assertEquals(100, e(10, 2)) 52 | assertEquals(1000, e(10, 3)) 53 | } 54 | } 55 | // END groovy_functional_strategy 56 | } 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /functional_thinking_examples/scala/complex_numbers/Complex.scala: -------------------------------------------------------------------------------- 1 | final class Complex(val real: Int, val imaginary: Int) extends Ordered[Complex] { 2 | 3 | def +(operand: Complex) = 4 | new Complex(real + operand.real, imaginary + operand.imaginary) 5 | 6 | def +(operand: Int) = 7 | new Complex(real + operand, imaginary) 8 | 9 | def -(operand: Complex) = 10 | new Complex(real - operand.real, imaginary - operand.imaginary) 11 | 12 | def -(operand: Int) = 13 | new Complex(real - operand, imaginary) 14 | 15 | def *(operand: Complex) = 16 | new Complex(real * operand.real - imaginary * operand.imaginary, 17 | real * operand.imaginary + imaginary * operand.real) 18 | 19 | override def toString() = 20 | real + (if (imaginary < 0) "" else "+") + imaginary + "i" 21 | 22 | override def equals(that: Any) = that match { 23 | case other : Complex => (real == other.real) && (imaginary == other.imaginary) 24 | case other : Int => (real == other) && (imaginary == 0) 25 | case _ => false 26 | } 27 | 28 | override def hashCode(): Int = 29 | 41 * ((41 + real) + imaginary) 30 | 31 | def compare(that: Complex) : Int = { 32 | def myMagnitude = Math.sqrt(real ^ 2 + imaginary ^ 2) 33 | def thatMagnitude = Math.sqrt(that.real ^ 2 + that.imaginary ^ 2) 34 | (myMagnitude - thatMagnitude).round.toInt 35 | } 36 | } -------------------------------------------------------------------------------- /functional_thinking_examples/groovy/list_perspective/ListPerspectiveDemo.groovy: -------------------------------------------------------------------------------- 1 | // BEGIN groovy_iterate 2 | def numbers = [6, 28, 4, 9, 12, 4, 8, 8, 11, 45, 99, 2] 3 | 4 | def iterateList(listOfNums) { 5 | listOfNums.each { n -> 6 | println "${n}" 7 | } 8 | } 9 | println "Iterate List" 10 | iterateList(numbers) 11 | // END groovy_iterate 12 | 13 | // BEGIN groovy_recurse 14 | def recurseList(listOfNums) { 15 | if (listOfNums.size == 0) return; 16 | println "${listOfNums.head()}" 17 | recurseList(listOfNums.tail()) 18 | } 19 | println "\nRecurse List" 20 | recurseList(numbers) 21 | // END groovy_recurse 22 | 23 | // BEGIN groovy_imp_filter 24 | def filterI(list, pred) { 25 | def new_list = [] 26 | list.each { i -> 27 | if (pred(i)) 28 | new_list << i 29 | } 30 | new_list 31 | } 32 | 33 | println "Imperative filtering" 34 | println filterI(1..20, {it % 2 == 0}) 35 | // [2, 4, 6, 8, 10, 12, 14, 16, 18, 20] 36 | // END groovy_imp_filter 37 | 38 | // BEGIN groovy_recurse_filter 39 | def filterR(list, pred) { 40 | if (list.size() == 0) return list 41 | if (pred(list.head())) 42 | [] + list.head() + filterR(list.tail(), pred) 43 | else 44 | filterR(list.tail(), pred) 45 | } 46 | 47 | println "Recursive Filtering" 48 | println filterR(1..20, {it % 2 == 0}) 49 | //// [2, 4, 6, 8, 10, 12, 14, 16, 18, 20] 50 | // END groovy_recurse_filter 51 | -------------------------------------------------------------------------------- /functional_thinking_examples/groovy/lazy_primes/LazyBuildup.groovy: -------------------------------------------------------------------------------- 1 | // BEGIN plazylist 2 | class PLazyList { 3 | private Closure list 4 | 5 | private PLazyList(list) { 6 | this.list = list 7 | } 8 | 9 | static PLazyList nil() { 10 | new PLazyList({-> []}) 11 | } 12 | 13 | PLazyList cons(head) { 14 | new PLazyList({-> [head, list]}) 15 | } 16 | 17 | def head() { 18 | def lst = list.call() 19 | lst ? lst[0] : null 20 | } 21 | 22 | def tail() { 23 | def lst = list.call() 24 | lst ? new PLazyList(lst.tail()[0]) : nil() 25 | } 26 | 27 | boolean isEmpty() { 28 | list.call() == [] 29 | } 30 | 31 | def fold(n, acc, f) { 32 | n == 0 || isEmpty() ? acc : tail().fold(n - 1, f.call(acc, head()), f) 33 | } 34 | 35 | def foldAll(acc, f) { 36 | isEmpty() ? acc : tail().foldAll(f.call(acc, head()), f) 37 | } 38 | 39 | def take(n) { 40 | fold(n, []) {acc, item -> acc << item} 41 | } 42 | 43 | def takeAll() { 44 | foldAll([]) {acc, item -> acc << item} 45 | } 46 | 47 | def toList() { 48 | takeAll() 49 | } 50 | } 51 | // END plazylist 52 | 53 | // BEGIN plazylist_demo 54 | def lazylist = PLazyList.nil().cons(4).cons(3).cons(2).cons(1) 55 | println(lazylist.takeAll()) 56 | println(lazylist.foldAll(0, {i, j -> i + j})) 57 | lazylist = PLazyList.nil().cons(1).cons(2).cons(4).cons(8) 58 | println(lazylist.take(2)) 59 | // END plazylist_demo 60 | -------------------------------------------------------------------------------- /functional_thinking_examples/java/number_classifier_totally_lazy/NumberClassifier.java: -------------------------------------------------------------------------------- 1 | package nealford.ft.totallylazy; 2 | 3 | // BEGIN number_classifier_totallylazy 4 | 5 | import com.googlecode.totallylazy.Predicate; 6 | import com.googlecode.totallylazy.Sequence; 7 | 8 | import static com.googlecode.totallylazy.Predicates.is; 9 | import static com.googlecode.totallylazy.numbers.Numbers.*; 10 | import static com.googlecode.totallylazy.predicates.WherePredicate.where; 11 | 12 | public class NumberClassifier { 13 | public static Predicate isFactor(Number n) { 14 | return where(remainder(n), is(zero)); // <1> 15 | } 16 | 17 | public static Sequence getFactors(final Number n) { 18 | return range(1, n).filter(isFactor(n)); 19 | } 20 | 21 | public static Sequence factors(final Number n) { 22 | return getFactors(n).memorise(); 23 | } 24 | 25 | public static Number aliquotSum(Number n) { 26 | return subtract(factors(n).reduce(sum), n); 27 | } 28 | 29 | public static boolean isPerfect(Number n) { 30 | return equalTo(n, aliquotSum(n)); 31 | } 32 | 33 | public static boolean isAbundant(Number n) { 34 | return greaterThan(aliquotSum(n), n); 35 | } 36 | 37 | public static boolean isDeficient(Number n) { 38 | return lessThan(aliquotSum(n), n); 39 | }} 40 | // END number_classifier_totallylazy 41 | -------------------------------------------------------------------------------- /functional_thinking_examples/java/number_classifier_functional_java/NumberClassifier.java: -------------------------------------------------------------------------------- 1 | package com.nealford.functionalthinking.numberclassifier; 2 | 3 | // BEGIN number_classifier_functional_java 4 | import fj.F; 5 | import fj.data.List; 6 | import static fj.data.List.range; 7 | 8 | public class NumberClassifier { 9 | 10 | // BEGIN functional_java_filter 11 | public List factorsOf(final int number) { 12 | return range(1, number + 1) // <1> 13 | .filter(new F() { 14 | public Boolean f(final Integer i) { 15 | return number % i == 0; 16 | } 17 | }); // <2> 18 | } 19 | // END functional_java_filter 20 | 21 | // BEGIN functional_java_fold 22 | public int aliquotSum(List factors) { // <3> 23 | return factors.foldLeft(fj.function.Integers.add, 0) - factors.last(); 24 | } 25 | // END functional_java_fold 26 | 27 | public boolean isPerfect(int number) { 28 | return aliquotSum(factorsOf(number)) == number; 29 | } 30 | 31 | public boolean isAbundant(int number) { 32 | return aliquotSum(factorsOf(number)) > number; 33 | } 34 | 35 | public boolean isDeficient(int number) { 36 | return aliquotSum(factorsOf(number)) < number; 37 | } 38 | } 39 | // END number_classifier_functional_java 40 | -------------------------------------------------------------------------------- /functional_thinking_examples/java/composition/ClassifierAlpha.java: -------------------------------------------------------------------------------- 1 | package com.nealford.conf.ft.composition; 2 | 3 | import java.util.HashSet; 4 | import java.util.Iterator; 5 | import java.util.Set; 6 | 7 | import static java.lang.Math.sqrt; 8 | 9 | // BEGIN java_classifier_alpha 10 | public class ClassifierAlpha { 11 | private int number; 12 | 13 | public ClassifierAlpha(int number) { 14 | this.number = number; 15 | } 16 | 17 | public boolean isFactor(int potential_factor) { 18 | return number % potential_factor == 0; 19 | } 20 | 21 | public Set factors() { 22 | HashSet factors = new HashSet<>(); 23 | for (int i = 1; i <= sqrt(number); i++) 24 | if (isFactor(i)) { 25 | factors.add(i); 26 | factors.add(number / i); 27 | } 28 | return factors; 29 | } 30 | 31 | static public int sum(Set factors) { 32 | Iterator it = factors.iterator(); 33 | int sum = 0; 34 | while (it.hasNext()) 35 | sum += (Integer) it.next(); 36 | return sum; 37 | } 38 | 39 | public boolean isPerfect() { 40 | return sum(factors()) - number == number; 41 | } 42 | 43 | public boolean isAbundant() { 44 | return sum(factors()) - number > number; 45 | } 46 | 47 | public boolean isDeficient() { 48 | return sum(factors()) - number < number; 49 | } 50 | } 51 | // END java_classifier_alpha 52 | -------------------------------------------------------------------------------- /functional_thinking_examples/java/wordfreq8/Words.java: -------------------------------------------------------------------------------- 1 | package com.nealford.functionalthinking.wordfreq; 2 | 3 | import java.util.*; 4 | import java.util.function.Consumer; 5 | import java.util.regex.Matcher; 6 | import java.util.regex.Pattern; 7 | import java.util.stream.Stream; 8 | import java.util.stream.StreamSupport; 9 | 10 | 11 | public class Words { 12 | private Set NON_WORDS = new HashSet() {{ 13 | add("the"); 14 | add("and"); 15 | add("of"); 16 | add("to"); 17 | add("a"); 18 | add("i"); 19 | add("it"); 20 | add("in"); 21 | add("or"); 22 | add("is"); 23 | add("d"); 24 | add("s"); 25 | add("as"); 26 | add("so"); 27 | add("but"); 28 | add("be"); 29 | }}; 30 | 31 | // BEGIN java_wordfreq8 32 | private List regexToList(String words, String regex) { 33 | List wordList = new ArrayList<>(); 34 | Matcher m = Pattern.compile(regex).matcher(words); 35 | while (m.find()) 36 | wordList.add(m.group()); 37 | return wordList; 38 | } 39 | 40 | public Map wordFreq(String words) { 41 | TreeMap wordMap = new TreeMap<>(); 42 | regexToList(words, "\\w+").stream() 43 | .map(w -> w.toLowerCase()) 44 | .filter(w -> !NON_WORDS.contains(w)) 45 | .forEach(w -> wordMap.put(w, wordMap.getOrDefault(w, 0) + 1)); 46 | return wordMap; 47 | } 48 | // END java_wordfreq8 49 | 50 | } 51 | -------------------------------------------------------------------------------- /functional_thinking_examples/groovy/expandounits/IntegerConversion.groovy: -------------------------------------------------------------------------------- 1 | import org.junit.Test 2 | 3 | import static org.junit.Assert.assertEquals 4 | import static org.junit.Assert.assertThat 5 | import static org.junit.Assert.assertTrue 6 | import static org.junit.Assert.assertTrue 7 | 8 | /** 9 | * (probably) Copyright 2013 by Neal Ford. All rights reserved. 10 | */ 11 | class IntegerConv { 12 | static Double getAsMeters(Integer self) { 13 | self * 0.30480 14 | } 15 | 16 | static Double getAsFeet(Integer self) { 17 | self * 3.2808 18 | } 19 | } 20 | 21 | // BEGIN groovy_integer_conv_test 22 | class IntegerConvTest{ 23 | 24 | static { 25 | Integer.metaClass.getAsM { -> 26 | delegate * 0.30480 27 | } 28 | 29 | Integer.metaClass.getAsFt { -> 30 | delegate * 3.2808 31 | } 32 | 33 | } 34 | 35 | 36 | @Test void test_conversion_with_expando() { 37 | assertTrue 1.asM == 0.30480 38 | assertTrue 1.asFt == 3.2808 39 | } 40 | 41 | @Test void test_conversion_with_category() { 42 | use(IntegerConv) { 43 | assertEquals(1 * 3.2808, 1.asFeet, 0.1) 44 | assertEquals(1 * 0.30480, 1.asMeters, 0.1) 45 | } 46 | } 47 | // END groovy_integer_conv_test 48 | 49 | @Test void expando_order() { 50 | try { 51 | 1.decode() 52 | } catch(NullPointerException ex) { 53 | println("can't decode with no parameters") 54 | } 55 | Integer.metaClass.decode { -> 56 | delegate * Math.PI; 57 | } 58 | assertEquals(1.decode(), Math.PI, 0.1) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /functional_thinking_examples/clojure/number_classifier/core.clj: -------------------------------------------------------------------------------- 1 | (ns number-classifier.core) 2 | 3 | ; BEGIN number_classifier_clojure 4 | (defn is-factor? [factor number] ;<1> 5 | (zero? (rem number factor))) ;<2> 6 | 7 | (defn factors [number] ;<3> 8 | (filter #(is-factor? % number) (range 1 (inc number)))) 9 | 10 | (defn aliquot-sum [number] ;<4> 11 | (- (reduce + (factors number)) number)) 12 | 13 | (defn perfect? [number] 14 | (= number (aliquot-sum number))) 15 | 16 | (defn abundant? [number] 17 | (< number (aliquot-sum number))) 18 | 19 | (defn deficient? [number] 20 | (> number (aliquot-sum number))) 21 | ; END number_classifier_clojure 22 | 23 | ; BEGIN clojure-fast-factors 24 | (defn fast-factors [number] 25 | (let [partial-sum (for [n (range 1 (inc (Math/sqrt number))) :when (is-factor? n number)] n)] 26 | (set (into partial-sum (map #(/ number %) partial-sum))))) 27 | ; END clojure-fast-factors 28 | 29 | (defn faliquot-sum [number] 30 | (- (reduce + (fast-factors number)) number)) 31 | 32 | (defn fast-perfect? [number] 33 | (= number (faliquot-sum number))) 34 | 35 | ; BEGIN clojure_classify 36 | (defn classify [num] 37 | (let [factors (->> (range 1 (inc num)) ; <1> 38 | (filter #(zero? (rem num %)))) ; <2> 39 | sum (reduce + factors) ; <3> 40 | aliquot-sum (- sum num)] ; <4> 41 | 42 | (cond ; <5> 43 | (= aliquot-sum num) :perfect 44 | (> aliquot-sum num) :abundant 45 | (< aliquot-sum num) :deficient))) 46 | ; END clojure_classify -------------------------------------------------------------------------------- /functional_thinking_examples/java/number_classifier/ImpNumberClassifierSimple.java: -------------------------------------------------------------------------------- 1 | package nealford.ft.numberclassification; 2 | 3 | // BEGIN imp_classifier 4 | import java.util.HashMap; 5 | import java.util.HashSet; 6 | import java.util.Map; 7 | import java.util.Set; 8 | 9 | public class ImpNumberClassifierSimple { 10 | private int _number; //<1> 11 | private Map _cache; //<2> 12 | 13 | public ImpNumberClassifierSimple(int targetNumber) { 14 | _number = targetNumber; 15 | _cache = new HashMap<>(); 16 | } 17 | 18 | public boolean isFactor(int potential) { 19 | return _number % potential == 0; 20 | } 21 | 22 | public Set getFactors() { 23 | Set factors = new HashSet<>(); 24 | factors.add(1); 25 | factors.add(_number); 26 | for (int i = 2; i < _number; i++) 27 | if (isFactor(i)) 28 | factors.add(i); 29 | return factors; 30 | } 31 | 32 | public int aliquotSum() { // <3> 33 | if (_cache.get(_number) == null) { 34 | int sum = 0; 35 | for (int i : getFactors()) 36 | sum += i; 37 | _cache.put(_number, sum - _number); 38 | } 39 | return _cache.get(_number); 40 | } 41 | 42 | public boolean isPerfect() { 43 | return aliquotSum() == _number; 44 | } 45 | 46 | public boolean isAbundant() { 47 | return aliquotSum() > _number; 48 | } 49 | 50 | public boolean isDeficient() { 51 | return aliquotSum() < _number; 52 | } 53 | } 54 | // END imp_classifier 55 | -------------------------------------------------------------------------------- /functional_thinking_examples/java/number_classifier_functional_java/NumberClassifierFJOptimized.java: -------------------------------------------------------------------------------- 1 | package com.nealford.functionalthinking.numberclassifier; 2 | 3 | // BEGIN number_classifier_functional_java_optimized 4 | import static fj.function.Integers.add; 5 | import static java.lang.Math.round; 6 | import static java.lang.Math.sqrt; 7 | import fj.F; 8 | import fj.data.List; 9 | import static fj.data.List.range; 10 | 11 | public class NumberClassifierFJOptimized { 12 | 13 | // BEGIN functional_java_factors_opt 14 | public static List factorsOf(final int number) { 15 | final List factors = range(1, (int) round(sqrt(number) + 1)) 16 | .filter(new F() { 17 | public Boolean f(final Integer i) { 18 | return number % i == 0; 19 | } 20 | }); 21 | return factors.append(factors.map(new F() { 22 | public Integer f(final Integer i) { 23 | return number / i; 24 | } 25 | })).nub(); 26 | } 27 | // END functional_java_factors_opt 28 | 29 | public static int aliquotSum(List factors) { 30 | return factors.foldLeft(add, 0) - factors.last(); 31 | } 32 | 33 | public static boolean isPerfect(int number) { 34 | return aliquotSum(factorsOf(number)) == number; 35 | } 36 | 37 | public static boolean isAbundant(int number) { 38 | return aliquotSum(factorsOf(number)) > number; 39 | } 40 | 41 | public static boolean isDeficient(int number) { 42 | return aliquotSum(factorsOf(number)) < number; 43 | } 44 | } 45 | // END number_classifier_functional_java_optimized 46 | -------------------------------------------------------------------------------- /functional_thinking_examples/java/number_classifier8/NumberClassifier.java: -------------------------------------------------------------------------------- 1 | package com.nealford.ft.nc8; 2 | 3 | // BEGIN number_classifier_java8 4 | import java.util.List; 5 | import java.util.stream.Collectors; 6 | import java.util.stream.IntStream; 7 | import java.util.stream.Stream; 8 | 9 | import static java.lang.Math.sqrt; 10 | import static java.util.stream.Collectors.toList; 11 | import static java.util.stream.IntStream.range; 12 | 13 | public class NumberClassifier { 14 | 15 | // BEGIN java8_filter 16 | public static IntStream factorsOf(int number) { 17 | return range(1, number + 1) 18 | .filter(potential -> number % potential == 0); 19 | } 20 | // END java8_filter 21 | 22 | public static int aliquotSum(int number) { 23 | return factorsOf(number).sum() - number; 24 | } 25 | 26 | public static boolean isPerfect(int number) { 27 | return aliquotSum(number) == number; 28 | } 29 | 30 | public static boolean isAbundant(int number) { 31 | return aliquotSum(number)> number; 32 | } 33 | 34 | public static boolean isDeficient(int number) { 35 | return aliquotSum(number) < number; 36 | } 37 | 38 | } 39 | // END number_classifier_java8 40 | 41 | // BEGIN java8_filter_fast 42 | // public static List fastFactorsOf(int number) { 43 | // List factors = range(1, (int) (sqrt(number) + 1)) 44 | // .filter(potential -> number % potential == 0) 45 | // .boxed() 46 | // .collect(toList()); 47 | // List factorsAboveSqrt = factors 48 | // .stream() 49 | // .map(e -> number / e).collect(toList()); 50 | // factors.addAll(factorsAboveSqrt); 51 | // return factors.stream().distinct().collect(toList()); 52 | // } 53 | // END java8_filter_fast 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /functional_thinking_examples/java/number_classifier/ImpNumberClassifier.java: -------------------------------------------------------------------------------- 1 | package nealford.ft.numberclassification; 2 | // BEGIN number_classifier_imperative_optimized 3 | 4 | import java.util.HashMap; 5 | import java.util.HashSet; 6 | import java.util.Map; 7 | import java.util.Set; 8 | 9 | import static java.lang.Math.sqrt; 10 | 11 | public class ImpNumberClassifier { 12 | private int _number; //<1> 13 | private Map _cache; //<2> 14 | 15 | public ImpNumberClassifier(int targetNumber) { 16 | _number = targetNumber; 17 | _cache = new HashMap<>(); 18 | } 19 | 20 | private boolean isFactor(int candidate) { 21 | return _number % candidate == 0; 22 | } 23 | 24 | private Set getFactors() { 25 | Set factors = new HashSet<>(); 26 | factors.add(1); 27 | factors.add(_number); 28 | for (int i = 2; i <= sqrt(_number); i++) //<3> 29 | if (isFactor(i)) { 30 | factors.add(i); 31 | factors.add(_number / i); 32 | } 33 | return factors; 34 | } 35 | 36 | private int aliquotSum() { 37 | int sum = 0; 38 | for (int i : getFactors()) 39 | sum += i; 40 | return sum - _number; 41 | } 42 | 43 | private int cachedAliquotSum() { //<4> 44 | if (_cache.containsKey(_number)) 45 | return _cache.get(_number); 46 | else { 47 | int sum = aliquotSum(); 48 | _cache.put(_number, sum); 49 | return sum; 50 | } 51 | } 52 | 53 | public boolean isPerfect() { 54 | return cachedAliquotSum() == _number; 55 | } 56 | 57 | public boolean isAbundant() { 58 | return cachedAliquotSum() > _number; 59 | } 60 | 61 | public boolean isDeficient() { 62 | return cachedAliquotSum() < _number; 63 | } 64 | } 65 | // END number_classifier_imperative_optimized -------------------------------------------------------------------------------- /functional_thinking_examples/java/errorhandling/RomanNumeralParser.java: -------------------------------------------------------------------------------- 1 | package com.nealford.ft.errorhandling; 2 | 3 | import fj.P1; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | public class RomanNumeralParser { 9 | // BEGIN java_romans_default 10 | private static final int MIN = 0; 11 | private static final int MAX = 1000; 12 | 13 | public static Either parseNumberDefaults(final String s) { 14 | if (! s.matches("[IVXLXCDM]+")) 15 | return Either.left(new Exception("Invalid Roman numeral")); 16 | else { 17 | int number = new RomanNumeral(s).toInt(); 18 | return Either.right(new RomanNumeral(number >= MAX ? MAX : number).toInt()); 19 | } 20 | } 21 | // END java_romans_default 22 | 23 | // BEGIN java_romans_parse 24 | public static Either parseNumber(String s) { 25 | if (! s.matches("[IVXLXCDM]+")) 26 | return Either.left(new Exception("Invalid Roman numeral")); 27 | else 28 | return Either.right(new RomanNumeral(s).toInt()); 29 | } 30 | // END java_romans_parse 31 | 32 | // BEGIN java_romans_lazy 33 | public static P1> parseNumberLazy(final String s) { 34 | if (! s.matches("[IVXLXCDM]+")) 35 | return new P1>() { 36 | public Either _1() { 37 | return Either.left(new Exception("Invalid Roman numeral")); 38 | } 39 | }; 40 | else 41 | return new P1>() { 42 | public Either _1() { 43 | return Either.right(new RomanNumeral(s).toInt()); 44 | } 45 | }; 46 | } 47 | // END java_romans_lazy 48 | 49 | 50 | // BEGIN java_romannum_divide 51 | public static Map divide(int x, int y) { 52 | Map result = new HashMap(); 53 | if (y == 0) 54 | result.put("exception", new Exception("div by zero")); 55 | else 56 | result.put("answer", (double) x / y); 57 | return result; 58 | } 59 | // END java_romannum_divide 60 | } 61 | -------------------------------------------------------------------------------- /functional_thinking_examples/java/CounterDemo.java: -------------------------------------------------------------------------------- 1 | 2 | 3 | import java.util.List; 4 | import java.util.ArrayList; 5 | 6 | // BEGIN counter_demo 7 | class Counter { 8 | public int varField; 9 | 10 | Counter(int var) { 11 | varField = var; 12 | } 13 | 14 | public static Counter makeCounter() { 15 | return new Counter(0); 16 | } 17 | 18 | public int execute() { 19 | return ++varField; 20 | } 21 | } 22 | // END counter_demo 23 | 24 | class AnonCounter { 25 | public int varField; 26 | 27 | public static AnonCounter makeCounter() { 28 | final int var = 0; 29 | return new AnonCounter() {{ 30 | varField = var; 31 | }}; 32 | } 33 | 34 | public int execute() { 35 | return ++varField; 36 | } 37 | } 38 | 39 | interface ListerFunc { 40 | public int call(int arg); 41 | public int getSum(); 42 | } 43 | 44 | class Adder implements ListerFunc { 45 | private int sum = 0; 46 | 47 | public int getSum() { 48 | return sum; 49 | } 50 | 51 | Adder() { 52 | sum = 0; 53 | } 54 | 55 | public int call(int arg) { 56 | return sum += arg; 57 | } 58 | } 59 | 60 | class ListApplier { 61 | public static int apply(List list, ListerFunc f) { 62 | for (int i : list) 63 | f.call(i); 64 | return f.getSum(); 65 | } 66 | } 67 | 68 | public class CounterDemo { 69 | 70 | public CounterDemo() { 71 | Counter c1, c2; 72 | c1 = Counter.makeCounter(); 73 | c1.execute(); 74 | c1.execute(); 75 | c1.execute(); 76 | 77 | c2 = Counter.makeCounter(); 78 | System.out.println("c1 = " + c1.execute() + ", c2 = " + c2.execute()); 79 | 80 | AnonCounter c3 = AnonCounter.makeCounter(); 81 | System.out.println("c3 (1) = " + c3.execute() + ", c3 (2) = " + c3.execute()); 82 | 83 | List list = new ArrayList(); 84 | for (int i = 0; i < 100; i++) 85 | list.add(i); 86 | 87 | int sum = ListApplier.apply(list, new Adder()); 88 | System.out.println("sum = " + sum); 89 | } 90 | 91 | public static void main(String[] args) { 92 | new CounterDemo(); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /functional_thinking_examples/groovy/allaboutlists/AllAboutListsTest.groovy: -------------------------------------------------------------------------------- 1 | import com.nealford.ft.allaboutlists.NumberClassification 2 | import com.nealford.ft.allaboutlists.NumberClassifier 3 | import org.junit.Test 4 | import com.nealford.ft.allaboutlists.LazyList 5 | import static junit.framework.Assert.assertEquals 6 | import static com.nealford.ft.allaboutlists.NumberClassifier.nextPerfectNumberAfter 7 | import static org.junit.Assert.assertTrue 8 | 9 | /** 10 | * (probably) Copyright 2011 by Neal Ford. All rights reserved. 11 | */ 12 | class AllAboutListsTest { 13 | // BEGIN groovy_lazy_list 14 | def prepend(val, closure) { new LazyList(val, closure) } 15 | 16 | def integers(n) { prepend(n, { integers(n + 1) }) } 17 | 18 | @Test 19 | public void lazy_list_acts_like_a_list() { 20 | def naturalNumbers = integers(1) 21 | assertEquals('1 2 3 4 5 6 7 8 9 10', naturalNumbers.getHead(10).join(' ')) 22 | def evenNumbers = naturalNumbers.filter { it % 2 == 0 } 23 | assertEquals('2 4 6 8 10 12 14 16 18 20', evenNumbers.getHead(10).join(' ')) 24 | } 25 | // END groovy_lazy_list 26 | 27 | @Test 28 | public void can_find_next_perfect_number() { 29 | assertTrue 6 == nextPerfectNumberAfter(1) 30 | assertTrue 28 == nextPerfectNumberAfter(6+1) 31 | assertTrue 496 == nextPerfectNumberAfter(28+1) 32 | } 33 | 34 | // BEGIN groovy_infinite_perfect 35 | def perfectNumbers(n) { prepend(n, 36 | { perfectNumbers(nextPerfectNumberAfter(n)) }) }; 37 | 38 | @Test 39 | public void infinite_perfect_number_sequence() { 40 | def perfectNumbers = perfectNumbers(nextPerfectNumberAfter(1)) 41 | assertEquals([6, 28, 496], perfectNumbers.getHead(3)) 42 | } 43 | // END groovy_infinite_perfect 44 | 45 | @Test 46 | public void terse_classify() { 47 | [1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 13, 14, 15, 48 | 16, 17, 19, 21, 22, 23, 25, 26, 27].each {i -> 49 | assertTrue(NumberClassifier.classify(i) == NumberClassification.DEFICIENT) 50 | } 51 | [ 52 | 12, 18, 20, 24, 30, 36, 40, 42, 48, 54, 56, 60, 66, 70, 72, 78, 80, 84, 88, 90, 96, 100, 102].each {i -> 53 | assertTrue(NumberClassifier.classify(i) == NumberClassification.ABUNDANT) 54 | } 55 | [6, 28, 496].each {i -> 56 | assertTrue(NumberClassifier.classify(i) == NumberClassification.PERFECT) 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /functional_thinking_examples/scala/indexOfAny/indexOfAny.scala: -------------------------------------------------------------------------------- 1 | // BEGIN scala_firstIndexOfAny 2 | def firstIndexOfAny(input : String, searchChars : Seq[Char]) : Option[Int] = { 3 | def indexedInput = (0 until input.length).zip(input) 4 | val result = for (pair <- indexedInput; 5 | char <- searchChars; 6 | if (char == pair._2)) yield (pair._1) 7 | if (result.isEmpty) 8 | None 9 | else 10 | Some(result.head) 11 | } 12 | // END scala_firstIndexOfAny 13 | 14 | // BEGIN scala_indexOfAny 15 | def indexOfAny(input : String, searchChars : Seq[Char]) : Seq[Int] = { 16 | def indexedInput = (0 until input.length).zip(input) 17 | for (pair <- indexedInput; 18 | char <- searchChars; 19 | if (char == pair._2)) yield (pair._1) 20 | } 21 | // END scala_indexOfAny 22 | 23 | def indexedInput(input : Seq[Char]) : Seq[Any] = { 24 | (0 until input.length).zip(input) 25 | } 26 | 27 | 28 | 29 | println("boundary conditions\n================") 30 | println(firstIndexOfAny("", "") match { case Some(matches) => "matches"; case None => "No values present" }, "expected No values present") 31 | println(firstIndexOfAny("", "a") match { case Some(matches) => "matches"; case None => "No values present" }, "expected No values present") 32 | println(firstIndexOfAny("z", "") match { case Some(matches) => "matches"; case None => "No values present" }, "expected No values present") 33 | println(firstIndexOfAny("aba", "z") match { case Some(matches) => "matches"; case None => "No values present" }, "expected No values present") 34 | 35 | println("\nfirstIndexOfAny tests\n================") 36 | println(firstIndexOfAny("zzabyycdxx", "za").get, "expected: 0") 37 | println(firstIndexOfAny("zzabyycdxx", "by").get, "expected: 3") 38 | println(firstIndexOfAny("zzyabyycdxx", "by").get, "expected: 2") 39 | 40 | 41 | println("\nindexOfAny tests\n================") 42 | println(indexOfAny("zzabyycdxx", ""), "expected: []") 43 | println(indexOfAny("", "za"), "expected: []") 44 | println(indexOfAny("", ""), "expected: []") 45 | println(indexOfAny("zzabyycdxx", "za"), "expected: [0 1 2]") 46 | println(indexOfAny("zzabyycdxx", "by"), "expected: [3 4 5]") 47 | println(indexOfAny("zzabyycdxx", "ax"), "expected: [2 8 9]") 48 | println(indexOfAny("zxzabyycdx", "ax"), "expected: [1 3 9]") 49 | 50 | println("\nindexed collection\n================") 51 | println(indexedInput("zabycdxx")) 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /functional_thinking_examples/java/number_classifier/NumberClassifier.java: -------------------------------------------------------------------------------- 1 | package nealford.ft.numberclassification; 2 | 3 | // BEGIN functional_number_classifier 4 | import java.util.Collection; 5 | import java.util.Collections; 6 | import java.util.HashSet; 7 | import java.util.Set; 8 | 9 | public class NumberClassifier { 10 | 11 | public static boolean isFactor(final int candidate, final int number) { //<1> 12 | return number % candidate == 0; 13 | } 14 | 15 | public static Set factors(final int number) { //<2> 16 | Set factors = new HashSet<>(); 17 | factors.add(1); 18 | factors.add(number); 19 | for (int i = 2; i < number; i++) 20 | if (isFactor(i, number)) 21 | factors.add(i); 22 | return factors; 23 | } 24 | 25 | public static int aliquotSum(final Collection factors) { 26 | int sum = 0; 27 | int targetNumber = Collections.max(factors); 28 | for (int n : factors) { //<3> 29 | sum += n; 30 | } 31 | return sum - targetNumber; 32 | } 33 | 34 | public static boolean isPerfect(final int number) { 35 | return aliquotSum(factors(number)) == number; 36 | } 37 | //<4> 38 | public static boolean isAbundant(final int number) { 39 | return aliquotSum(factors(number)) > number; 40 | } 41 | 42 | public static boolean isDeficient(final int number) { 43 | return aliquotSum(factors(number)) < number; 44 | } 45 | } 46 | // END functional_number_classifier 47 | -------------------------------------------------------------------------------- /functional_thinking_examples/java/number_classifier/NumberClassifierOptimized.java: -------------------------------------------------------------------------------- 1 | package nealford.ft.numberclassification; 2 | 3 | import java.util.Collection; 4 | import java.util.HashSet; 5 | import java.util.Set; 6 | 7 | import static java.lang.Math.sqrt; 8 | 9 | // BEGIN number_classifier_functional_optimized 10 | public class NumberClassifierOptimized { 11 | 12 | public static boolean isFactor(final int candidate, final int number) { 13 | return number % candidate == 0; 14 | } 15 | 16 | public static Set factors(final int number) { 17 | Set factors = new HashSet<>(); 18 | factors.add(1); 19 | factors.add(number); 20 | for (int i = 2; i <= sqrt(number); i++) 21 | if (isFactor(i, number)) { 22 | factors.add(i); 23 | factors.add(number / i); 24 | } 25 | return factors; 26 | } 27 | 28 | public static int sum(final Collection numbers) { 29 | int sum = 0; 30 | for (int n : numbers) 31 | sum += n; 32 | return sum; 33 | } 34 | 35 | public static boolean isPerfect(final int number) { 36 | return sum(factors(number)) - number == number; 37 | } 38 | 39 | public static boolean isAbundant(final int number) { 40 | return sum(factors(number)) - number > number; 41 | } 42 | 43 | public static boolean isDeficient(final int number) { 44 | return sum(factors(number)) - number < number; 45 | } 46 | } 47 | // END number_classifier_functional_optimized -------------------------------------------------------------------------------- /functional_thinking_examples/java/structuralpatternmatching/Trees.java: -------------------------------------------------------------------------------- 1 | package com.nealford.ft.structuralpatternmatching; 2 | 3 | import fj.data.Either; 4 | 5 | import static com.nealford.ft.structuralpatternmatching.Tree.Empty; 6 | import static com.nealford.ft.structuralpatternmatching.Tree.Leaf; 7 | import static com.nealford.ft.structuralpatternmatching.Tree.Node; 8 | import static java.lang.Math.max; 9 | 10 | public class Trees { 11 | 12 | // BEGIN java_eithertree_depth 13 | static public int depth(Tree t) { 14 | for (Empty e : t.toEither().left()) 15 | return 0; 16 | for (Either ln: t.toEither().right()) { 17 | for (Leaf leaf : ln.left()) 18 | return 1; 19 | for (Node node : ln.right()) 20 | return 1 + max(depth(node.left), depth(node.right)); 21 | } 22 | throw new RuntimeException("Inexhaustible pattern match on tree"); 23 | } 24 | // END java_eithertree_depth 25 | 26 | // BEGIN java_eithertree_intree 27 | static public boolean inTree(Tree t, int value) { 28 | for (Empty e : t.toEither().left()) 29 | return false; 30 | for (Either ln: t.toEither().right()) { 31 | for (Leaf leaf : ln.left()) 32 | return value == leaf.n; 33 | for (Node node : ln.right()) 34 | return inTree(node.left, value) | inTree(node.right, value); 35 | } 36 | return false; 37 | } 38 | // END java_eithertree_intree 39 | 40 | // BEGIN java_eithertree_occurrences 41 | static public int occurrencesIn(Tree t, int value) { 42 | for (Empty e: t.toEither().left()) 43 | return 0; 44 | for (Either ln: t.toEither().right()) { 45 | for (Leaf leaf : ln.left()) 46 | if (value == leaf.n) return 1; 47 | for (Node node : ln.right()) 48 | return occurrencesIn(node.left, value) 49 | + occurrencesIn(node.right, value); 50 | } 51 | return 0; 52 | } 53 | // END java_eithertree_occurrences 54 | 55 | static public String printTree(Tree t, String output) { 56 | for (Empty e: t.toEither().left()) 57 | return output; 58 | for (Either ln: t.toEither().right()) { 59 | for (Leaf leaf : ln.left()) 60 | return output + String.valueOf(leaf.n) + "\n"; 61 | for (Node node : ln.right()) 62 | return "." + printTree(node.left, output) + printTree(node.right, output); 63 | } 64 | return ""; 65 | } 66 | 67 | public static void main(String[] args) { 68 | Tree t = new Node(new Node(new Node(new Leaf(4),new Node(new Leaf(1), new Node(new Node(new Node(new Node( 69 | new Node(new Node(new Leaf(10), new Leaf(0)), new Leaf(22)), new Node(new Node(new Node(new Leaf(4), 70 | new Empty()), new Leaf(101)), new Leaf(555))), new Leaf(201)), new Leaf(1000)), new Leaf(4)))), 71 | new Leaf(12)), new Leaf(27)); 72 | System.out.println(printTree(t, "")); 73 | } 74 | 75 | 76 | } 77 | -------------------------------------------------------------------------------- /functional_thinking_examples/groovy/FlyweightTest.groovy: -------------------------------------------------------------------------------- 1 | package com.nealford.ft.patterns 2 | 3 | // BEGIN groovy_flyweight_defs 4 | class Computer { 5 | def type 6 | def cpu 7 | def memory 8 | def hardDrive 9 | def cd 10 | } 11 | 12 | class Desktop extends Computer { 13 | def driveBays 14 | def fanWattage 15 | def videoCard 16 | } 17 | 18 | class Laptop extends Computer { 19 | def usbPorts 20 | def dockingBay 21 | } 22 | 23 | class AssignedComputer { 24 | def computerType 25 | def userId 26 | 27 | public AssignedComputer(computerType, userId) { 28 | this.computerType = computerType 29 | this.userId = userId 30 | } 31 | } 32 | // END groovy_flyweight_defs 33 | 34 | // BEGIN groovy_computer_factory 35 | class CompFactory { 36 | def types = [:] 37 | static def instance; 38 | 39 | private ComputerFactory() { 40 | def laptop = new Laptop() 41 | def tower = new Desktop() 42 | types.put("MacBookPro6_2", laptop) 43 | types.put("SunTower", tower) 44 | } 45 | 46 | static def getInstance() { 47 | if (instance == null) 48 | instance = new CompFactory() 49 | instance 50 | } 51 | 52 | def ofType(computer) { 53 | types[computer] 54 | } 55 | } 56 | // END groovy_computer_factory 57 | 58 | // BEGIN groovy_factory_singleton 59 | @Singleton class ComputerFactory { 60 | def types = [:] 61 | 62 | private ComputerFactory() { 63 | def laptop = new Laptop() 64 | def tower = new Desktop() 65 | types.put("MacBookPro6_2", laptop) 66 | types.put("SunTower", tower) 67 | } 68 | 69 | def ofType(computer) { 70 | types[computer] 71 | } 72 | } 73 | // END groovy_factory_singleton 74 | 75 | 76 | class FlyweightTest extends GroovyTestCase { 77 | // BEGIN groovy_memoization_of_flyweights 78 | def computerOf = {type -> 79 | def of = [MacBookPro6_2: new Laptop(), SunTower: new Desktop()] 80 | return of[type] 81 | } 82 | 83 | def computerOfType = computerOf.memoize() 84 | // END groovy_memoization_of_flyweights 85 | 86 | // BEGIN groovy_flyweight_test 87 | @Test 88 | public void comp_factory() { 89 | def bob = new AssignedComputer( 90 | CompFactory.instance.ofType("MacBookPro6_2"), "Bob") 91 | def steve = new AssignedComputer( 92 | CompFactory.instance.ofType("MacBookPro6_2"), "Steve") 93 | assertTrue(bob.computerType == steve.computerType) 94 | } 95 | // END groovy_flyweight_test 96 | 97 | // BEGIN groovy_flyweight_demo 98 | @Test 99 | public void flyweight_computers() { 100 | def bob = new AssignedComputer( 101 | ComputerFactory.instance.ofType("MacBookPro6_2"), "Bob") 102 | def steve = new AssignedComputer( 103 | ComputerFactory.instance.ofType("MacBookPro6_2"), "Steve") 104 | assertTrue(bob.computerType == steve.computerType) 105 | 106 | def sally = new AssignedComputer( 107 | computerOfType("MacBookPro6_2"), "Sally") 108 | def betty = new AssignedComputer( 109 | computerOfType("MacBookPro6_2"), "Betty") 110 | assertTrue sally.computerType == betty.computerType 111 | } 112 | // END groovy_flyweight_demo 113 | } 114 | -------------------------------------------------------------------------------- /functional_thinking_examples/groovy/patterns_adaptor/pegs_closures_test.groovy: -------------------------------------------------------------------------------- 1 | import org.junit.Test 2 | import org.junit.runner.JUnitCore 3 | import static org.junit.Assert.* 4 | import static junit.framework.Assert.*; 5 | import static org.hamcrest.CoreMatchers.* 6 | import static org.junit.matchers.JUnitMatchers.* 7 | import pegs.*; 8 | 9 | // BEGIN groovy_cubething 10 | class CubeThing { 11 | def x, y, z 12 | } 13 | // END groovy_cubething 14 | 15 | class PegsClosuresTest { 16 | 17 | // BEGIN groovy_pegs_closure_test 18 | static { 19 | SquarePeg.metaClass.getRadius = { -> 20 | Math.sqrt(((delegate.width/2) ** 2)*2) 21 | } 22 | } 23 | 24 | @Test void expando_adapter() { 25 | def hole = new RoundHole(radius:4.0) 26 | (4..7).each { w -> 27 | def peg = new SquarePeg(width:w) 28 | if (w < 6) 29 | assertTrue hole.pegFits(peg) 30 | else 31 | assertFalse hole.pegFits(peg) 32 | } 33 | } 34 | // END groovy_pegs_closure_test 35 | 36 | // BEGIN groovy_dyn_adaptor 37 | def roundPegOf(squarePeg) { 38 | [getRadius:{Math.sqrt( 39 | ((squarePeg.width/2) ** 2)*2)}] as RoundThing 40 | 41 | } 42 | 43 | @Test void functional_adaptor() { 44 | def hole = new RoundHole(radius:4.0) 45 | (4..7).each { w -> 46 | def peg = roundPegOf(new SquarePeg(width:w)) 47 | if (w < 6) 48 | assertTrue hole.pegFits(peg) 49 | else 50 | assertFalse hole.pegFits(peg) 51 | } 52 | } 53 | // END groovy_dyn_adaptor 54 | 55 | // BEGIN groovy_all_functional 56 | def pegFits(peg, hole) { 57 | Math.sqrt(((peg.width/2) ** 2)*2) <= hole.radius 58 | } 59 | 60 | @Test void functional_all_the_way() { 61 | def hole = new RoundHole(radius:4.0) 62 | (4..7).each { w -> 63 | def peg = new SquarePeg(width:w) 64 | if (w < 6) 65 | assertTrue pegFits(peg, hole) 66 | else 67 | assertFalse pegFits(peg, hole) 68 | } 69 | } 70 | // END groovy_all_functional 71 | 72 | // BEGIN groovy_func_comp 73 | def asSquare(peg) { 74 | [getWidth:{peg.x}] as SquarePeg 75 | } 76 | 77 | def asRound(peg) { 78 | [getRadius:{Math.sqrt( 79 | ((peg.width/2) ** 2)*2)}] as RoundThing 80 | } 81 | // END groovy_func_comp 82 | 83 | @Test void functional_composition() { 84 | def hole = new RoundHole(radius:4.0) 85 | (4..7).each { w -> 86 | def cube = new CubeThing(x:w) 87 | if (w < 6) 88 | assertTrue pegFits(asSquare(cube), hole) 89 | else 90 | assertFalse pegFits(asSquare(cube), hole) 91 | } 92 | } 93 | 94 | // BEGIN groovy_func_comp_test 95 | @Test 96 | void mixed_functional_composition() { 97 | def hole = new RoundHole(radius:4.0) 98 | (4..7).each { w -> 99 | def cube = new CubeThing(x:w) 100 | if (w < 6) 101 | assertTrue hole.pegFits(asRound(asSquare(cube))) 102 | else 103 | assertFalse hole.pegFits(asRound(asSquare(cube))) 104 | } 105 | 106 | } 107 | // END groovy_func_comp_test 108 | 109 | } 110 | 111 | JUnitCore.main('PegsClosuresTest') 112 | -------------------------------------------------------------------------------- /functional_thinking_examples/groovy/metafunctionaljava/IntegerClassifierTest.groovy: -------------------------------------------------------------------------------- 1 | package com.nealford.ft.metafunctionaljava 2 | 3 | import org.junit.Test 4 | import static junit.framework.Assert.assertTrue 5 | import static org.junit.Assert.assertFalse 6 | import fj.data.Stream 7 | import static fj.data.Stream.cons 8 | 9 | import static com.nealford.ft.metafunctionaljava.Classifier.nextPerfectNumberFrom 10 | import static org.junit.Assert.assertEquals 11 | 12 | /** 13 | * (probably) Copyright 2011 by Neal Ford. All rights reserved. 14 | */ 15 | class IntegerClassifierTest { 16 | static { 17 | Integer.metaClass.isPerfect = {-> 18 | Classifier.isPerfect(delegate) 19 | } 20 | 21 | Integer.metaClass.isAbundant = {-> 22 | Classifier.isAbundant(delegate) 23 | } 24 | 25 | Integer.metaClass.isDeficient = {-> 26 | Classifier.isDeficient(delegate) 27 | } 28 | } 29 | 30 | @Test 31 | void classifier_classifies_correctly() { 32 | assertTrue Classifier.isPerfect(6) 33 | assertTrue Classifier.isPerfect(496) 34 | assertTrue Classifier.isDeficient(7) 35 | assertTrue Classifier.isAbundant(12) 36 | } 37 | 38 | @Test 39 | void metaclass_classifiers() { 40 | def num = 28 41 | assertTrue num.isPerfect() 42 | assertTrue 7.isDeficient() 43 | assertTrue 6.isPerfect() 44 | assertTrue 12.isAbundant() 45 | } 46 | 47 | @Test 48 | void curry_to_create_classifier() { 49 | def perfect6 = Classifier.&isPerfect.curry(6) 50 | def deficient6 = Classifier.&isDeficient.curry(6) 51 | assertTrue perfect6() 52 | assertFalse deficient6() 53 | } 54 | 55 | // BEGIN groovy_metafunctional_stream 56 | static { 57 | Stream.metaClass.filter = { c -> delegate.filter(c as fj.F) } 58 | // Stream.metaClass.filter = { Closure c -> delegate.filter(c as fj.F) } 59 | Stream.metaClass.getAt = { n -> delegate.index(n) } 60 | Stream.metaClass.getAt = { Range r -> r.collect { delegate.index(it) } } 61 | } 62 | 63 | @Test 64 | void adding_methods_to_fj_classes() { 65 | def evens = Stream.range(0).filter { it % 2 == 0 } 66 | assertTrue(evens.take(5).asList() == [0, 2, 4, 6, 8]) 67 | assertTrue((8..12).collect { evens[it] } == [16, 18, 20, 22, 24]) 68 | assertTrue(evens[3..6] == [6, 8, 10, 12]) 69 | } 70 | // END groovy_metafunctional_stream 71 | 72 | @Test 73 | void next_perfect_number() { 74 | assertTrue Classifier.nextPerfectNumberFrom(1) == 6 75 | assertTrue Classifier.nextPerfectNumberFrom(6) == 28 76 | assertTrue Classifier.nextPerfectNumberFrom(28) == 496 77 | } 78 | 79 | // BEGIN groovy_add_fj_list 80 | static { 81 | Stream.metaClass.asList = { delegate.toCollection().asList() } 82 | // Stream.metaClass.static.cons = 83 | // { head, Closure c -> delegate.cons(head, ['_1':c] as fj.P1)} 84 | Stream.metaClass.static.cons = 85 | { head, closure -> delegate.cons(head, closure as fj.P1) } 86 | } 87 | // END groovy_add_fj_list 88 | // BEGIN groovy_fj_infinite_perfect 89 | def perfectNumbers(num) { 90 | cons(nextPerfectNumberFrom(num), { perfectNumbers(nextPerfectNumberFrom(num))}) 91 | } 92 | 93 | @Test 94 | void infinite_stream_of_perfect_nums_using_funtional_java() { 95 | assertEquals([6, 28, 496], perfectNumbers(1).take(3).asList()) 96 | } 97 | // END groovy_fj_infinite_perfect 98 | 99 | } 100 | -------------------------------------------------------------------------------- /functional_thinking_examples/java/errorhandling/FjRomanNumeralParser.java: -------------------------------------------------------------------------------- 1 | package com.nealford.ft.errorhandling; 2 | 3 | import fj.P1; 4 | import fj.data.Either; 5 | import fj.data.Option; 6 | 7 | public class FjRomanNumeralParser { 8 | private static final int MIN = 0; 9 | private static final int MAX = 1000; 10 | 11 | public static Either parseNumber(String s) { 12 | if (! s.matches("[IVXLXCDM]+")) 13 | return Either.left(new Exception("Invalid Roman numeral")); 14 | else 15 | return Either.right(new RomanNumeral(s).toInt()); 16 | } 17 | 18 | public static P1> parseNumberLazy(final String s) { 19 | if (! s.matches("[IVXLXCDM]+")) 20 | return new P1>() { 21 | public Either _1() { 22 | return Either.left(new Exception("Invalid Roman numeral")); 23 | } 24 | }; 25 | else 26 | return new P1>() { 27 | public Either _1() { 28 | return Either.right(new RomanNumeral(s).toInt()); 29 | } 30 | }; 31 | } 32 | 33 | public static Either parseNumberDefaults(final String s) { 34 | if (! s.matches("[IVXLXCDM]+")) 35 | return Either.left(new Exception("Invalid Roman numeral")); 36 | else { 37 | int number = new RomanNumeral(s).toInt(); 38 | return Either.right(new RomanNumeral(number >= MAX ? MAX : number).toInt()); 39 | } 40 | } 41 | 42 | // BEGIN java_fj_divide_exception 43 | public static Either divide(int x, int y) { 44 | try { 45 | return Either.right(x / y); 46 | } catch (Exception e) { 47 | return Either.left(e); 48 | } 49 | } 50 | // END java_fj_divide_exception 51 | 52 | // BEGIN java_fj_option 53 | public static Option divide(double x, double y) { 54 | if (y == 0) 55 | return Option.none(); 56 | return Option.some(x / y); 57 | } 58 | // END java_fj_option 59 | 60 | // BEGIN java_fj_romans_divide_lazy 61 | public static P1> divideLazily(final int x, final int y) { 62 | return new P1>() { 63 | public Either _1() { 64 | try { 65 | return Either.right(x / y); 66 | } catch (Exception e) { 67 | return Either.left(e); 68 | } 69 | } 70 | }; 71 | } 72 | // END java_fj_romans_divide_lazy 73 | 74 | // BEGIN java_fj_next_exceptions 75 | public static Either> 76 | divideRoman(final String x, final String y) { 77 | Either possibleX = parseNumber(x); 78 | Either possibleY = parseNumber(y); 79 | if (possibleX.isLeft() || possibleY.isLeft()) 80 | return Either.left(new NumberFormatException("invalid parameter")); 81 | int intY = possibleY.right().value().intValue(); 82 | Either errorForY = 83 | Either.left(new ArithmeticException("div by 1")); 84 | if (intY == 1) 85 | return Either.right((fj.data.Either) errorForY); 86 | int intX = possibleX.right().value().intValue(); 87 | Either result = 88 | Either.right(new Double((double) intX) / intY); 89 | return Either.right(result); 90 | } 91 | // END java_fj_next_exceptions 92 | 93 | } 94 | -------------------------------------------------------------------------------- /functional_thinking_examples/java/errorhandling/EitherTest.java: -------------------------------------------------------------------------------- 1 | package com.nealford.ft.errorhandling; 2 | 3 | import fj.P1; 4 | import org.junit.Test; 5 | 6 | import java.util.Map; 7 | 8 | import static org.junit.Assert.assertEquals; 9 | import static org.junit.Assert.assertSame; 10 | import static org.junit.Assert.assertTrue; 11 | 12 | public class EitherTest { 13 | private static final String INVALID_ROMAN_NUMERAL = "Invalid Roman numeral"; 14 | 15 | // BEGIN java_divide_test 16 | @Test 17 | public void maps_success() { 18 | Map result = RomanNumeralParser.divide(4, 2); 19 | assertEquals(2.0, (Double) result.get("answer"), 0.1); 20 | } 21 | 22 | @Test 23 | public void maps_failure() { 24 | Map result = RomanNumeralParser.divide(4, 0); 25 | assertEquals("div by zero", ((Exception) result.get("exception")).getMessage()); 26 | } 27 | // END java_divide_test 28 | 29 | 30 | @Test 31 | public void either_left() { 32 | final String[] result = new String[1]; 33 | Either e = Either.left("foo"); 34 | e.fold( 35 | new F() { 36 | public void f(String x) { 37 | result[0] = x; 38 | } 39 | }, 40 | new F() { 41 | public void f(Integer x) { 42 | result[0] = "Integer: " + x; 43 | } 44 | }); 45 | assertEquals(result[0], "foo"); 46 | } 47 | 48 | @Test 49 | public void either_right() { 50 | final String[] result = new String[1]; 51 | Either e = Either.right(4); 52 | e.fold( 53 | new F() { 54 | public void f(String x) { 55 | result[0] = x; 56 | } 57 | }, 58 | new F() { 59 | public void f(Integer x) { 60 | result[0] = "Integer: " + x; 61 | } 62 | }); 63 | assertEquals(result[0], "Integer: 4"); 64 | } 65 | 66 | // BEGIN java_romans_parsing_test 67 | @Test 68 | public void parsing_success() { 69 | Either result = RomanNumeralParser.parseNumber("XLII"); 70 | assertEquals(Integer.valueOf(42), result.right()); 71 | } 72 | 73 | @Test 74 | public void parsing_failure() { 75 | Either result = RomanNumeralParser.parseNumber("FOO"); 76 | assertEquals(INVALID_ROMAN_NUMERAL, result.left().getMessage()); 77 | } 78 | // END java_romans_parsing_test 79 | 80 | // BEGIN java_romans_parse_lazy_test 81 | @Test 82 | public void parse_lazy() { 83 | P1> result = 84 | RomanNumeralParser.parseNumberLazy("XLII"); 85 | assertEquals(42, result._1().right().intValue()); 86 | } 87 | 88 | @Test 89 | public void parse_lazy_exception() { 90 | P1> result = 91 | RomanNumeralParser.parseNumberLazy("FOO"); 92 | assertTrue(result._1().isLeft()); 93 | assertEquals(INVALID_ROMAN_NUMERAL, result._1().left().getMessage()); 94 | } 95 | // END java_romans_parse_lazy_test 96 | 97 | // BEGIN java_romans_defaults_test 98 | @Test 99 | public void parse_defaults_normal() { 100 | Either result = 101 | RomanNumeralParser.parseNumberDefaults("XLII"); 102 | assertEquals(42, result.right().intValue()); 103 | } 104 | @Test 105 | public void parse_defaults_triggered() { 106 | Either result = 107 | RomanNumeralParser.parseNumberDefaults("MM"); 108 | assertEquals(1000, result.right().intValue()); 109 | } 110 | // END java_romans_defaults_test 111 | } 112 | -------------------------------------------------------------------------------- /functional_thinking_examples/java/errorhandling/RomanNumeral.java: -------------------------------------------------------------------------------- 1 | package com.nealford.ft.errorhandling; 2 | 3 | public class RomanNumeral { 4 | 5 | private static final String NUMERAL_MUST_BE_POSITIVE = 6 | "Value of RomanNumeral must be positive."; 7 | private static final String NUMERAL_MUST_BE_3999_OR_LESS = 8 | "Value of RomanNumeral must be 3999 or less."; 9 | private static final String DOES_NOT_DEFINE_A_ROMAN_NUMERAL = 10 | "An empty string does not define a Roman numeral."; 11 | private static final String NO_NEGATIVE_ROMAN_NUMERALS = 12 | "Negative numbers not allowed"; 13 | private static final String NUMBER_FORMAT_EXCEPTION = 14 | "Illegal character in Roman numeral."; 15 | 16 | private final int num; 17 | 18 | private static int[] numbers = {1000, 900, 500, 400, 100, 90, 19 | 50, 40, 10, 9, 5, 4, 1}; 20 | private static String[] letters = {"M", "CM", "D", "CD", "C", "XC", 21 | "L", "XL", "X", "IX", "V", "IV", "I"}; 22 | 23 | 24 | public RomanNumeral(int arabic) { 25 | if (arabic < 1) 26 | throw new NumberFormatException(NUMERAL_MUST_BE_POSITIVE); 27 | if (arabic > 3999) 28 | throw new NumberFormatException(NUMERAL_MUST_BE_3999_OR_LESS); 29 | num = arabic; 30 | } 31 | 32 | 33 | public RomanNumeral(String roman) { 34 | if (roman.length() == 0) 35 | throw new NumberFormatException(DOES_NOT_DEFINE_A_ROMAN_NUMERAL); 36 | if (roman.charAt(0) == '-') 37 | throw new NumberFormatException(NO_NEGATIVE_ROMAN_NUMERALS); 38 | 39 | roman = roman.toUpperCase(); 40 | 41 | int positionInString = 0; 42 | int arabicEquivalent = 0; 43 | 44 | while (positionInString < roman.length()) { 45 | char letter = roman.charAt(positionInString); 46 | int number = letterToNumber(letter); 47 | if (number < 0) 48 | throw new NumberFormatException(NUMBER_FORMAT_EXCEPTION); 49 | positionInString++; 50 | if (positionInString == roman.length()) 51 | arabicEquivalent += number; 52 | else { 53 | int nextNumber = letterToNumber(roman.charAt(positionInString)); 54 | if (nextNumber > number) { 55 | arabicEquivalent += (nextNumber - number); 56 | positionInString++; 57 | } else 58 | arabicEquivalent += number; 59 | } 60 | } 61 | 62 | if (arabicEquivalent > 3999) 63 | throw new NumberFormatException(NUMERAL_MUST_BE_3999_OR_LESS); 64 | num = arabicEquivalent; 65 | } 66 | 67 | private int letterToNumber(char letter) { 68 | switch (letter) { 69 | case 'I': 70 | return 1; 71 | case 'V': 72 | return 5; 73 | case 'X': 74 | return 10; 75 | case 'L': 76 | return 50; 77 | case 'C': 78 | return 100; 79 | case 'D': 80 | return 500; 81 | case 'M': 82 | return 1000; 83 | default: 84 | return -1; 85 | } 86 | } 87 | 88 | public String toString() { 89 | String romanNumeral = ""; 90 | int remainingPartToConvert = num; 91 | for (int i = 0; i < numbers.length; i++) { 92 | while (remainingPartToConvert >= numbers[i]) { 93 | romanNumeral += letters[i]; 94 | remainingPartToConvert -= numbers[i]; 95 | } 96 | } 97 | return romanNumeral; 98 | } 99 | 100 | public int toInt() { 101 | return num; 102 | } 103 | } -------------------------------------------------------------------------------- /functional_thinking_examples/java/structuralpatternmatching/TreeTest.java: -------------------------------------------------------------------------------- 1 | import com.nealford.ft.structuralpatternmatching.Tree; 2 | import fj.data.Either; 3 | import org.junit.Test; 4 | 5 | import static com.nealford.ft.structuralpatternmatching.Tree.Empty; 6 | import static com.nealford.ft.structuralpatternmatching.Tree.Leaf; 7 | import static com.nealford.ft.structuralpatternmatching.Tree.Node; 8 | 9 | import static com.nealford.ft.structuralpatternmatching.Trees.depth; 10 | import static com.nealford.ft.structuralpatternmatching.Trees.occurrencesIn; 11 | import static com.nealford.ft.structuralpatternmatching.Trees.inTree; 12 | import static junit.framework.Assert.assertEquals; 13 | import static junit.framework.Assert.assertFalse; 14 | import static junit.framework.Assert.assertTrue; 15 | 16 | /** 17 | * (probably) Copyright 2012 by Neal Ford. All rights reserved. 18 | */ 19 | public class TreeTest { 20 | 21 | @Test 22 | public void depth_of_0_test() { 23 | Tree t = new Empty(); 24 | assertEquals(0, depth(t)); 25 | } 26 | 27 | @Test 28 | public void depth_of_1_test() { 29 | Tree t = new Node(new Empty(), new Empty()); 30 | assertEquals(1, depth(t)); 31 | } 32 | 33 | @Test 34 | public void depth_of_3_test() { 35 | Tree t = new Node(new Empty(), new Node(new Leaf(5), new Empty())); 36 | assertEquals(3, depth(t)); 37 | } 38 | 39 | @Test 40 | public void depth_of_4_test() { 41 | Tree t = new Node(new Empty(), new Node(new Empty(), new Node(new Empty(), new Leaf(4)))); 42 | assertEquals(4, depth(t)); 43 | } 44 | 45 | @Test 46 | public void depth_of_5_with_hetero_tree_test() { 47 | Tree t = new Node( 48 | new Node( 49 | new Node( 50 | new Node( 51 | new Node(new Leaf(4),new Empty()), new Leaf(12)), new Leaf(55)), new Empty()), new Leaf(4)); 52 | assertEquals(6, depth(t)); 53 | } 54 | 55 | @Test 56 | public void different_structure() { 57 | Tree t = new Node(new Leaf(5), new Empty()); 58 | assertEquals(2,depth(t)); 59 | } 60 | 61 | @Test 62 | public void search_test() { 63 | Tree t = new Node(new Empty(), new Leaf(4)); 64 | assertEquals(1, occurrencesIn(t, 4)); 65 | assertEquals(0, occurrencesIn(t, 32)); 66 | } 67 | 68 | @Test 69 | public void more_elaborate_search_test() { 70 | Tree t = new Node(new Node(new Node(new Node( 71 | new Node(new Leaf(4),new Empty()), 72 | new Leaf(12)), new Leaf(55)), 73 | new Empty()), new Leaf(4)); 74 | assertEquals(1, occurrencesIn(t, 55)); 75 | assertEquals(2, occurrencesIn(t, 4)); 76 | assertEquals(1, occurrencesIn(t, 12)); 77 | assertEquals(0, occurrencesIn(t, 42)); 78 | } 79 | 80 | @Test 81 | public void boolean_search_test() { 82 | Tree t = new Node(new Empty(), new Leaf(4)); 83 | assertTrue(inTree(t, 4)); 84 | assertFalse(inTree(t, 42)); 85 | } 86 | 87 | 88 | // BEGIN java_eithertrees_search_test 89 | @Test 90 | public void more_elaborate_searchp_test() { 91 | Tree t = new Node(new Node(new Node(new Node( 92 | new Node(new Leaf(4),new Empty()), 93 | new Leaf(12)), new Leaf(55)), 94 | new Empty()), new Leaf(4)); 95 | assertTrue(inTree(t, 55)); 96 | assertTrue(inTree(t, 4)); 97 | assertTrue(inTree(t, 12)); 98 | assertFalse(inTree(t, 42)); 99 | } 100 | // END java_eithertrees_search_test 101 | 102 | // BEGIN java_eithertrees_branch_test 103 | @Test 104 | public void multi_branch_tree_test() { 105 | Tree t = new Node(new Node(new Node(new Leaf(4), 106 | new Node(new Leaf(1), new Node( 107 | new Node(new Node(new Node( 108 | new Node(new Node(new Leaf(10), new Leaf(0)), 109 | new Leaf(22)), new Node(new Node( 110 | new Node(new Leaf(4), new Empty()), 111 | new Leaf(101)), new Leaf(555))), 112 | new Leaf(201)), new Leaf(1000)), 113 | new Leaf(4)))), 114 | new Leaf(12)), new Leaf(27)); 115 | assertEquals(12, depth(t)); 116 | assertTrue(inTree(t, 555)); 117 | assertEquals(3, occurrencesIn(t, 4)); 118 | } 119 | // END java_eithertrees_branch_test 120 | } 121 | -------------------------------------------------------------------------------- /functional_thinking_examples/java/errorhandling/FjEitherTest.java: -------------------------------------------------------------------------------- 1 | package com.nealford.ft.errorhandling; 2 | 3 | import com.nealford.ft.errorhandling.FjRomanNumeralParser; 4 | import fj.F; 5 | import fj.P1; 6 | import fj.data.Either; 7 | import fj.data.Option; 8 | import org.junit.Ignore; 9 | import org.junit.Test; 10 | 11 | import static org.junit.Assert.assertEquals; 12 | import static org.junit.Assert.assertTrue; 13 | 14 | public class FjEitherTest { 15 | private static final String INVALID_ROMAN_NUMERAL = "Invalid Roman numeral"; 16 | 17 | @Test 18 | public void parsing_success() { 19 | Either result = FjRomanNumeralParser.parseNumber("XLII"); 20 | assertEquals(Integer.valueOf(42), result.right().value()); 21 | } 22 | 23 | @Test 24 | public void parsing_failure() { 25 | Either result = FjRomanNumeralParser.parseNumber("FOO"); 26 | assertEquals(INVALID_ROMAN_NUMERAL, result.left().value().getMessage()); 27 | } 28 | 29 | @Test 30 | public void parse_lazy() { 31 | P1> result = FjRomanNumeralParser.parseNumberLazy("XLII"); 32 | assertEquals((long) 42, (long) result._1().right().value()); 33 | } 34 | 35 | @Test 36 | public void parse_lazy_exception() { 37 | P1> result = FjRomanNumeralParser.parseNumberLazy("FOO"); 38 | assertTrue(result._1().isLeft()); 39 | assertEquals(INVALID_ROMAN_NUMERAL, result._1().left().value().getMessage()); 40 | } 41 | 42 | @Test 43 | public void parse_defaults_normal() { 44 | Either result = FjRomanNumeralParser.parseNumberDefaults("XLII"); 45 | assertEquals((long) 42, (long) result.right().value()); 46 | } 47 | 48 | @Test 49 | public void parse_defaults_triggered() { 50 | Either result = FjRomanNumeralParser.parseNumberDefaults("MM"); 51 | assertEquals((long) 1000, (long) result.right().value()); 52 | } 53 | 54 | // BEGIN java_fj_divide_exception_test 55 | @Test 56 | public void catching_other_people_exceptions() { 57 | Either result = FjRomanNumeralParser.divide(4, 2); 58 | assertEquals((long) 2, (long) result.right().value()); 59 | Either failure = FjRomanNumeralParser.divide(4, 0); 60 | assertEquals("/ by zero", failure.left().value().getMessage()); 61 | } 62 | // END java_fj_divide_exception_test 63 | 64 | // BEGIN java_fj_divide_lazy_exception_test 65 | @Test 66 | public void lazily_catching_other_peoples_exceptions() { 67 | P1> result = FjRomanNumeralParser.divideLazily(4, 2); 68 | assertEquals((long) 2, (long) result._1().right().value()); 69 | P1> failure = FjRomanNumeralParser.divideLazily(4, 0); 70 | assertEquals("/ by zero", failure._1().left().value().getMessage()); 71 | } 72 | // END java_fj_divide_lazy_exception_test 73 | 74 | // BEGIN java_fj_nested_exception_test 75 | private P1> divide(final double x, final double y) { 76 | return new P1>() { 77 | public Either _1() { 78 | try { 79 | return Either.right(x / y); 80 | } catch (Exception e) { 81 | return Either.left(e); 82 | } 83 | } 84 | }; 85 | } 86 | 87 | @Test 88 | public void test_divide_romans_success() { 89 | fj.data.Either> result = FjRomanNumeralParser.divideRoman("IV", "II"); 90 | assertEquals(2.0,result.right().value().right().value().doubleValue(), 0.1); 91 | } 92 | 93 | @Test 94 | public void test_divide_romans_number_format_error() { 95 | Either> result = FjRomanNumeralParser.divideRoman("IVooo", "II"); 96 | assertEquals("invalid parameter", result.left().value().getMessage()); 97 | } 98 | 99 | @Test 100 | public void test_divide_romans_arthmetic_exception() { 101 | Either> result = FjRomanNumeralParser.divideRoman("IV", "I"); 102 | assertEquals("div by 1", result.right().value().left().value().getMessage()); 103 | } 104 | // END java_fj_nested_exception_test 105 | // 106 | // @Ignore("can't get final assertion to work because of type system wonkery") 107 | // @Test 108 | // public void lift_other_functions() { 109 | // F sin = new F() { 110 | // public Double f(final Double a) { 111 | // return Math.sin(a); 112 | // } 113 | // }; 114 | //// P1> lifter = divide(4, 2).map(Either.rightMap_().f(sin)); 115 | // } 116 | 117 | 118 | // BEGIN java_fj_option_test 119 | @Test 120 | public void option_test_success() { 121 | Option result = FjRomanNumeralParser.divide(4.0, 2); 122 | assertEquals(2.0, (Double) result.some(), 0.1); 123 | } 124 | 125 | @Test 126 | public void option_test_failure() { 127 | Option result = FjRomanNumeralParser.divide(4.0, 0); 128 | assertEquals(Option.none(), result); 129 | } 130 | // END java_fj_option_test 131 | 132 | 133 | } 134 | -------------------------------------------------------------------------------- /functional_thinking_examples/groovy/memoization/ClassifierTest.groovy: -------------------------------------------------------------------------------- 1 | import com.nealford.ft.memoization.Classifier 2 | 3 | import static junit.framework.Assert.assertTrue 4 | 5 | import org.junit.Test 6 | import com.nealford.ft.memoization.ClassifierMemoizedSum 7 | import com.nealford.ft.memoization.ClassifierMemoized 8 | import com.nealford.ft.memoization.ClassifierCachedSum 9 | import com.nealford.ft.memoization.ClassifierFast 10 | 11 | /** 12 | * (probably) Copyright 2011 by Neal Ford. All rights reserved. 13 | */ 14 | class ClassifierTest { 15 | def classifier = new ClassifierCachedSum() 16 | def start; 17 | 18 | @Test 19 | void classifies_correctly() { 20 | assertTrue(Classifier.isPerfect(6)) 21 | assertTrue(Classifier.isPerfect(496)) 22 | assertTrue(Classifier.isPerfect(8128)) 23 | assertTrue(Classifier.isAbundant(24)) 24 | assertTrue(Classifier.isDeficient(7)) 25 | } 26 | 27 | @Test 28 | void optimizations() { 29 | assertTrue(ClassifierFast.isPerfect(6)) 30 | assertTrue(ClassifierFast.isPerfect(496)) 31 | assertTrue(ClassifierFast.isPerfect(8128)) 32 | assertTrue(ClassifierFast.isAbundant(24)) 33 | assertTrue(ClassifierFast.isDeficient(16)) 34 | assertTrue(ClassifierFast.isDeficient(7)) 35 | 36 | } 37 | 38 | // BEGIN groovy_uncached_sum 39 | def static final TEST_NUMBER_MAX = 5000 40 | 41 | @Test 42 | void mashup() { 43 | println "Test for range 1-${TEST_NUMBER_MAX}" 44 | print "Non-optimized: " 45 | start = System.currentTimeMillis() 46 | (1..TEST_NUMBER_MAX).each {n -> 47 | if (Classifier.isPerfect(n)) print '!' 48 | else if (Classifier.isAbundant(n)) print '+' 49 | else if (Classifier.isDeficient(n)) print '-' 50 | } 51 | println "\n\t ${System.currentTimeMillis() - start} ms" 52 | print "Non-optimized (2nd): " 53 | start = System.currentTimeMillis() 54 | (1..TEST_NUMBER_MAX).each {n -> 55 | if (Classifier.isPerfect(n)) print '!' 56 | else if (Classifier.isAbundant(n)) print '+' 57 | else if (Classifier.isDeficient(n)) print '-' 58 | } 59 | println "\n\t ${System.currentTimeMillis() - start} ms" 60 | // END groovy_uncached_sum 61 | 62 | // BEGIN groovy_cached_sum 63 | print "Cached sum: " 64 | def start = System.currentTimeMillis() 65 | (1..TEST_NUMBER_MAX).each {n -> 66 | if (classifier.isPerfect(n)) print '!' 67 | else if (classifier.isAbundant(n)) print '+' 68 | else if (classifier.isDeficient(n)) print '-' 69 | } 70 | println "\n\t ${System.currentTimeMillis() - start} ms" 71 | print "Cached sum (2nd run): " 72 | start = System.currentTimeMillis() 73 | (1..TEST_NUMBER_MAX).each {n -> 74 | if (classifier.isPerfect(n)) print '!' 75 | else if (classifier.isAbundant(n)) print '+' 76 | else if (classifier.isDeficient(n)) print '-' 77 | } 78 | println "\n\t ${System.currentTimeMillis() - start} ms" 79 | // END groovy_cached_sum 80 | 81 | // BEGIN groovy_cached_fully 82 | print "Cached: " 83 | def classifierFullyCached = new ClassifierCached() 84 | start = System.currentTimeMillis() 85 | (1..TEST_NUMBER_MAX).each {n -> 86 | if (classifierFullyCached.isPerfect(n)) print '!' 87 | else if (classifierFullyCached.isAbundant(n)) print '+' 88 | else if (classifierFullyCached.isDeficient(n)) print '-' 89 | } 90 | println "\n\t ${System.currentTimeMillis() - start} ms" 91 | print "Cached (2nd run): " 92 | start = System.currentTimeMillis() 93 | (1..TEST_NUMBER_MAX).each {n -> 94 | if (classifierFullyCached.isPerfect(n)) print '!' 95 | else if (classifierFullyCached.isAbundant(n)) print '+' 96 | else if (classifierFullyCached.isDeficient(n)) print '-' 97 | } 98 | println "\n\t ${System.currentTimeMillis() - start} ms" 99 | // END groovy_cached_fully 100 | 101 | // BEGIN groovy_partially_memoized 102 | print "Partially Memoized: " 103 | start = System.currentTimeMillis() 104 | (1..TEST_NUMBER_MAX).each {n -> 105 | if (ClassifierMemoizedSum.isPerfect(n)) print '!' 106 | else if (ClassifierMemoizedSum.isAbundant(n)) print '+' 107 | else if (ClassifierMemoizedSum.isDeficient(n)) print '-' 108 | } 109 | println "\n\t ${System.currentTimeMillis() - start} ms" 110 | print "Partially Memoized (2nd): " 111 | start = System.currentTimeMillis() 112 | (1..TEST_NUMBER_MAX).each {n -> 113 | if (ClassifierMemoizedSum.isPerfect(n)) print '!' 114 | else if (ClassifierMemoizedSum.isAbundant(n)) print '+' 115 | else if (ClassifierMemoizedSum.isDeficient(n)) print '-' 116 | } 117 | println "\n\t ${System.currentTimeMillis() - start} ms" 118 | 119 | print "Memoized: " 120 | start = System.currentTimeMillis() 121 | (1..TEST_NUMBER_MAX).each {n -> 122 | if (ClassifierMemoized.isPerfect(n)) print '!' 123 | else if (ClassifierMemoized.isAbundant(n)) print '+' 124 | else if (ClassifierMemoized.isDeficient(n)) print '-' 125 | } 126 | println "\n\t ${System.currentTimeMillis() - start} ms" 127 | print "Memoized(2nd) " 128 | start = System.currentTimeMillis() 129 | (1..TEST_NUMBER_MAX).each {n -> 130 | if (ClassifierMemoized.isPerfect(n)) print '!' 131 | else if (ClassifierMemoized.isAbundant(n)) print '+' 132 | else if (ClassifierMemoized.isDeficient(n)) print '-' 133 | } 134 | println "\n\t ${System.currentTimeMillis() - start} ms" 135 | // END groovy_partially_memoized 136 | } 137 | 138 | } 139 | --------------------------------------------------------------------------------