Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Introduction to Functional Programming with Scheme

7,483 views

Published on

Slide deck from my session at CodeMash 2.0.1.0

Published in: Technology
  • Be the first to comment

Introduction to Functional Programming with Scheme

  1. DocOnDev An Introduction to Functional Programming with Scheme Michael Norton michael@docondev.com : mail docondev.blogspot.com/ : blog twitter.com/DocOnDev : the twitters CodeMash 2.0.1.0
  2. DocOnDev SICP Study Gamma Group CodeMash 2.0.1.0
  3. DocOnDev Yes! Gamma Group Week 1 CodeMash 2.0.1.0
  4. DocOnDev umm... Gamma Group Week 3 CodeMash 2.0.1.0
  5. DocOnDev Hello? Gamma Group Week 5 CodeMash 2.0.1.0
  6. DocOnDev Maths are hard Second order nonlinear ordinary differential equation Third Order Nonlinear Partial Differential Equation CodeMash 2.0.1.0
  7. DocOnDev SICP I don’t recommend this text to start with CodeMash 2.0.1.0
  8. DocOnDev Texts I do recommend CodeMash 2.0.1.0
  9. DocOnDev Functional Programming CodeMash 2.0.1.0
  10. DocOnDev Consists Entirely of Functions CodeMash 2.0.1.0
  11. DocOnDev Consists Entirely of Functions Higher-order Functions CodeMash 2.0.1.0
  12. DocOnDev Favors Immutability CodeMash 2.0.1.0
  13. DocOnDev Referentially Transparent CodeMash 2.0.1.0
  14. DocOnDev Limited Loop Structures CodeMash 2.0.1.0
  15. DocOnDev Functional Programming • Consists Entirely of Functions • Favors Immutability • Referentially Transparent • Limited Loop Structures CodeMash 2.0.1.0
  16. DocOnDev LISP CodeMash 2.0.1.0
  17. DocOnDev Symbolic Expressions CodeMash 2.0.1.0
  18. DocOnDev Dynamically Typed CodeMash 2.0.1.0
  19. DocOnDev List Processing LISt Processing CodeMash 2.0.1.0
  20. DocOnDev Lisp • Symbolic Expressions • Dynamically Typed • List Processing CodeMash 2.0.1.0
  21. DocOnDev Scheme CodeMash 2.0.1.0
  22. DocOnDev Static (Lexical) Scope CodeMash 2.0.1.0
  23. DocOnDev Static Scope (define x 9) (define (show-x) (display x)) (define test (lambda (x) (show-x) (display ‘,)(display x)) (test 3) => 9,3 CodeMash 2.0.1.0
  24. DocOnDev Tail Recursion CodeMash 2.0.1.0
  25. DocOnDev Tail Recursion (define (factorial n) (define (fact-tail n m) (if (= n 0) m (fact-tail (- n 1) (* n m)))) (fact-tail n 1)) (factorial 4) => 24 CodeMash 2.0.1.0
  26. DocOnDev Shared Namespace CodeMash 2.0.1.0
  27. DocOnDev Scheme • Static Scope • Tail Recursion • Shared Namespace CodeMash 2.0.1.0
  28. DocOnDev String Calculator Kata CodeMash 2.0.1.0
  29. DocOnDev Requirements Takes a String of integers and returns their sum Can handle an unknown amount of integers Can handle a custom delimiter Delimiter notation “//[delimiter]n[numbers]” CodeMash 2.0.1.0
  30. DocOnDev First Test string-calculator-tests.ss #lang scheme (require (planet schematics/schemeunit:3) (planet schematics/schemeunit:3/text-ui) "string-calculator.ss") (check-equal? (str-calc "") 0 "Empty string should return 0") CodeMash 2.0.1.0
  31. DocOnDev FAIL! . expand: unbound identifier in module in: str-calc CodeMash 2.0.1.0
  32. DocOnDev String Calculator Code string-calculator.ss #lang scheme ;; String Calculator Kata (define (str-calc input) 0) ;; Expose these functions (provide str-calc) CodeMash 2.0.1.0
  33. DocOnDev PASS! #t CodeMash 2.0.1.0
  34. DocOnDev Second Test string-calculator-tests.ss #lang scheme (require ... "string-calculator.ss") (check-equal? (str-calc "") 0 "Should return 0") (check-equal? (str-calc "1") 1 "Should return 1") CodeMash 2.0.1.0
  35. DocOnDev FAIL! #t -------------------- FAILURE name: check-equal? location: (#<path:/string-calculator-tests.ss> 11 0 219 63) expression: (check-equal? (str-calc "1") 1) params: (0 1) message: "Should return 1" actual: 0 expected: 1 -------------------- CodeMash 2.0.1.0
  36. DocOnDev String Calculator Code string-calculator.ss #lang scheme (define (str-calc input) (cond ((= (string-length input) 0) 0) (else (string->number input)))) (provide str-calc) CodeMash 2.0.1.0
  37. DocOnDev PASS! #t #t CodeMash 2.0.1.0
  38. DocOnDev Third Test string-calculator-tests.ss #lang scheme (require ... "string-calculator.ss") (check-equal? (str-calc "") 0 "Should return 0") (check-equal? (str-calc "1") 1 "Should return 1") (check-equal? (str-calc "1,2") 3 "Should sum items") CodeMash 2.0.1.0
  39. DocOnDev FAIL! #t #t -------------------- FAILURE name: check-equal? location: (#<path:/string-calculator-tests.ss> 10 0 241 52) expression: (check-equal? (str-calc "1,2") 3) params: (#f 3) message: "Should sum items" actual: #f expected: 3 -------------------- CodeMash 2.0.1.0
  40. DocOnDev String Calculator Code string-calculator.ss (define (str-calc input) (cond ((list? input) (cond ((null? input) 0) (else (+ (string->number (car input)) (str-calc (cdr input)))))) ((= (string-length input) 0) 0) (else (str-calc (str-split input #,))))) CodeMash 2.0.1.0
  41. DocOnDev PASS! #t #t #t CodeMash 2.0.1.0
  42. DocOnDev Requirements Takes a String of integers and returns their sum Can handle an unknown amount of integers Can handle a custom delimiter Delimiter notation “//[delimiter]n[numbers]” CodeMash 2.0.1.0
  43. DocOnDev Fourth Test string-calculator-tests.ss #lang scheme (require ... "string-calculator.ss") (check-equal? (str-calc "") 0 "Should return 0") (check-equal? (str-calc "1") 1 "Should return 1") (check-equal? (str-calc "1,2") 3 "Should sum items") (check-equal? (str-calc "1,2,3,4") 10 "Should sum all items") CodeMash 2.0.1.0
  44. DocOnDev PASS! #t #t #t #t CodeMash 2.0.1.0
  45. DocOnDev Requirements Takes a String of integers and returns their sum Can handle an unknown amount of integers Can handle a custom delimiter Delimiter notation “//[delimiter]n[numbers]” CodeMash 2.0.1.0
  46. DocOnDev Fifth Test string-calculator-tests.ss #lang scheme (require ... "string-calculator.ss") (check-equal? (str-calc "") 0 "Should return 0") (check-equal? (str-calc "1") 1 "Should return 1") (check-equal? (str-calc "1,2") 3 "Should sum items") (check-equal? (str-calc "1,2,3,4") 10 "Should sum all items") (check-equal? (str-calc "1*2" #*) 3 "Should delimit on custom") CodeMash 2.0.1.0
  47. DocOnDev FAIL! . procedure str-calc: expects 1 argument, given 2: "1*2" #* CodeMash 2.0.1.0
  48. DocOnDev String Calculator Code string-calculator.ss (define (str-calc input [delim #,]) (cond ((list? input) (cond ((null? input) 0) (else (+ (string->number (car input)) (str-calc (cdr input)))))) ((= (string-length input) 0) 0) (else (str-calc (str-split input delim))))) CodeMash 2.0.1.0
  49. DocOnDev PASS! #t #t #t #t #t CodeMash 2.0.1.0
  50. DocOnDev Sixth Test string-calculator-tests.ss (require ... "string-calculator.ss") (check-equal? (str-calc "") 0 "Should return 0") (check-equal? (str-calc "1") 1 "Should return 1") (check-equal? (str-calc "1,2") 3 "Should sum items") (check-equal? (str-calc "1,2,3,4") 10 "Should sum all items") (check-equal? (str-calc "1*2" #*) 3 "Should delimit on custom") (check-equal? (str-calc "1*2*3*4" #*) 10 "Should delimit on custom") CodeMash 2.0.1.0
  51. DocOnDev PASS! #t #t #t #t #t #t CodeMash 2.0.1.0
  52. DocOnDev Requirements Takes a String of integers and returns their sum Can handle an unknown amount of integers Can handle a custom delimiter Delimiter notation “//[delimiter]n[numbers]” CodeMash 2.0.1.0
  53. DocOnDev Change Tests string-calculator-tests.ss (require ... "string-calculator.ss") (check-equal? (str-calc "") 0 "Should return 0") (check-equal? (str-calc "1") 1 "Should return 1") (check-equal? (str-calc "1,2") 3 "Should sum items") (check-equal? (str-calc "1,2,3,4") 10 "Should sum all items") (check-equal? (str-calc "//*n1*2") 3 "Should delimit on custom") (check-equal? (str-calc "//*n1*2*3*4") 10 "Should delimit on custom") CodeMash 2.0.1.0
  54. DocOnDev String Calculator Code string-calculator.ss (define (str-calc input) (cond ((list? input) (cond ((null? input) 0) (else (+ (string->number (car input)) (str-calc (cdr input)))))) ((= (string-length input) 0) 0) (else (str-calc (str-split (rm-delim input) (get-delim input))))) CodeMash 2.0.1.0
  55. DocOnDev Delimiter Parser delim-parse.ss (define (rm-delim input) (cadr (split-delim input))) (define (get-delim input) (car (split-delim input))) (define (split-delim input) (cond ((and (> (string-length input) 2) (string=? (substring input 0 2) "//")) (regexp-split #rx"n" (substring input 2))) (else (cons "," (list input))))) CodeMash 2.0.1.0
  56. DocOnDev Delimiter Tests delim-parse-tests.ss (require (planet schematics/schemeunit:3) (planet schematics/schemeunit:3/text-ui) "delimiter-parse.ss") (check-equal? (get-delim "1") #, "Default delimiter") (check-equal? (remove-delim "2,1") "2,1" "String") (check-equal? (get-delim "//*n2*1") #* "Delimiter") (check-equal? (remove-delim "//*n2*1") "2*1" "-Delimiter") CodeMash 2.0.1.0
  57. DocOnDev PASS! #t #t #t #t #t #t CodeMash 2.0.1.0
  58. DocOnDev Requirements Takes a String of integers and returns their sum Can handle an unknown amount of integers Can handle a custom delimiter Delimiter notation “//[delimiter]n[numbers]” CodeMash 2.0.1.0
  59. DocOnDev Ruby - Mario Aquino module Calculator def self.calculate(expression) raise ArgumentError, 'Must be a string' unless expression.is_a? String raise ArgumentError, 'Must be numeric' unless expression =~ pattern value = nil expression.scan(pattern) do |left, operator, right| value ||= left value = eval(value + operator + right).to_s end value end private def self.pattern /(d+)s?([+-*/])s*(?=(d+))/ end end CodeMash 2.0.1.0
  60. DocOnDev Python - Gary Bernhardt def add(string): string = _normalize_delimiters(string) if string: return _add_numbers_in_string(string) else: return 0 def _normalize_delimiters(string): string = _normalize_custom_delimiter(string) string = string.replace('n', ',') return string def _normalize_custom_delimiter(string): if string.startswith('//'): delimiter_spec, string = string.split('n', 1) delimiter = delimiter_spec[2:] string = string.replace(delimiter, ',') return string def _add_numbers_in_string(string): numbers = map(int, string.split(',')) _validate_numbers(numbers) return sum(numbers) def _validate_numbers(numbers): if any(number < 0 for number in numbers): raise ValueError CodeMash 2.0.1.0
  61. DocOnDev F# - Mark Needham module FSharpCalculator open System open System.Text.RegularExpressions   let split (delimeter:array<string>) (value:string) = value.Split (delimeter, StringSplitOptions.None) let toDecimal value = Decimal.Parse value   let (|CustomDelimeter|NoCustomDelimeter|) (value:string) = let delimeters (value:string) = Regex.Matches(value, "[([^]]*)]") |> Seq.cast |> Seq.map (fun (x:Match) -> x.Groups) |> Seq.map (fun x -> x |> Seq.cast<Group> |> Seq.nth 1) |> Seq.map (fun x -> x.Value) |> Seq.to_array   if (value.Length > 2 && "//".Equals(value.Substring(0, 2))) then if ("[".Equals(value.Substring(2,1))) then CustomDelimeter(delimeters value) else CustomDelimeter([| value.Substring(2, value.IndexOf("n") - 2) |]) else NoCustomDelimeter(",")   let digits value = match value with | CustomDelimeter(delimeters) -> value.Substring(value.IndexOf("n")) |> split delimeters |> Array.map toDecimal | NoCustomDelimeter(delimeter) -> value.Replace("n", delimeter) |> split [|delimeter |] |> Array.map toDecimal   let buildExceptionMessage negatives = sprintf "No negative numbers allowed. You provided %s" (String.Join(",", negatives |> Array.map (fun x -> x.ToString())))   let (|ContainsNegatives|NoNegatives|) digits = if (digits |> Array.exists (fun x -> x < 0.0m)) then ContainsNegatives(digits |> Array.filter (fun x -> x < 0.0m)) else NoNegatives(digits)   let add value = if ("".Equals(value) or "n".Equals(value)) then 0.0m else match digits value |> Array.filter (fun x -> x < 1000m) with | ContainsNegatives(negatives) -> raise (ArgumentException (buildExceptionMessage negatives)) | NoNegatives(digits) -> digits |> Array.sum CodeMash 2.0.1.0
  62. DocOnDev C# - Anonymous using System; using System.Collections.Generic; using System.Text; using System.Text.RegularExpressions; namespace TDDFun { public class StringCalculator { public double Add(string numbers) { Double sum = 0; if (string.IsNullOrEmpty(numbers)) { sum = 0; } else { const string regex1 = “(//)”; const string regex2 = “(.*?)”; const string regex3 = “(n)”; const string regex4 = “(.*?d)”; var delimiters = new List<string>(); var r = new Regex(regex1 + regex2 + regex3 + regex4, RegexOptions.IgnoreCase | RegexOptions.Singleline); Match m = r.Match(numbers); if (m.Success) { string delimiterCollection = m.Groups[2].ToString(); int numberStartIndex = m.Groups[3].Index; const string re5 = “([.*?])”; var r2 = new Regex(re5, RegexOptions.IgnoreCase | RegexOptions.Singleline); CodeMash 2.0.1.0
  63. DocOnDev C# - Anonymous (cont.) MatchCollection m2 = r2.Matches(delimiterCollection); if (m2.Count > 0) { foreach (Match x in m2) { delimiters.Add(x.ToString().Replace(“[", "").Replace("]“, “”)); } } else { delimiters.Add(delimiterCollection); } numbers = numbers.Remove(0, numberStartIndex + 1); } else { delimiters.Add(“n”); delimiters.Add(“,”); } string[] splittedNumbers = numbers.Split(delimiters.ToArray(), StringSplitOptions.None); ValiateNumbers(splittedNumbers); foreach (string s in splittedNumbers) { double ss = Double.Parse(s); sum += ss <= 1000 ? ss : 0; } } return sum; } CodeMash 2.0.1.0
  64. DocOnDev C# - Anonymous (cont.) private static void ValiateNumbers(IEnumerable<string> numbers) { double x; var negativeNumbers = new List<string>(); foreach (string s in numbers) { Validator.IsRequiredField(Double.TryParse(s, out x), “Validation Error”); if (Double.Parse(s) < 0) { negativeNumbers.Add(s); } } Validator.IsRequiredField(negativeNumbers.Count <= 0, “Negatives not allowed “ + ShowAllNegatives(negativeNumbers)); } private static string ShowAllNegatives(List<string> negativeNumbers) { var sb = new StringBuilder(); int counter = 0; negativeNumbers.ForEach(k => { if (counter == 0) { sb.Append(k); counter++; } else { sb.Append(“;” + k); } CodeMash 2.0.1.0
  65. DocOnDev C# - Anonymous (cont.) }); return sb.ToString(); } } public static class Validator { public static void IsRequiredField(bool criteria, string message) { if (!criteria) { throw new ValidationException(message); } } } public class ValidationException : ApplicationException { public ValidationException(string message) : base(message) { } } } CodeMash 2.0.1.0
  66. DocOnDev Thank You! Michael Norton michael@docondev.com : mail docondev.blogspot.com/ : blog twitter.com/DocOnDev : the twitters CodeMash 2.0.1.0
  67. DocOnDev References • Wikipedia http://en.wikipedia.org/wiki/Scheme • PLT Scheme http://plt-scheme.org/ • MIT Press SICP http://mitpress.mit.edu/sicp/ • MIT Press HTDP http://www.htdp.org/ • U of CA, Berkley http://www.cs.berkeley.edu/~bh/ss-toc2.html CodeMash 2.0.1.0

×