Created
July 2, 2020 22:20
-
-
Save Kazark/e830f52d769976fb54fb35b80555ee80 to your computer and use it in GitHub Desktop.
A playful implementation of square root in Guile, with a Haskell flavor
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#| An obfuscated Haskell-style solution to exercise 1.7 from SICP in Guile. | |
| | |
| The basis of this solution is the idea: the numerical method for square root | |
| does not inherently have any notion of what is means for the solution to be | |
| "good enough"; that is an _orthogonal_ concern. The inherent idea, in its | |
| purest form, is a limit of the algorithm considered as a function of the | |
| number of iterations. Therefore, it would be pleasing in our implementation | |
| to divorce the idea of what "good enough" means from our expression of the | |
| algorithm. But this requires an infinite data structure, because if we do not | |
| have an infinite data structure, we must from the first think about when to | |
| stop! | |
| | |
| Secondly, how do we know if we are close, without knowing what the answer is? | |
| Assuming a monotonic behavior of the square root approximation w.r.t. | |
| iterations (which this algorithm exhibits) then without actually knowing the | |
| _precise_ answer, the derivative of our algorithm considered as a function of | |
| the number of iterations will tell us how much closer each iteration is | |
| getting us. We then have a way of knowing when we are close, because if our | |
| monotonic function is not changing much, we are by definition close. | |
| | |
| For our infinite data structure, we will use Haskell-style lists, a.k.a. | |
| streams: that is, either a delayed '(), or a delayed cons cell. Thus, the | |
| empty stream is | |
| | |
| (delay '()) | |
| | |
| A stream that corresponds to (list 'foo 'bar 'baz) is | |
| | |
| (delay (: 'foo (delay (: 'bar (delay (: 'baz (delay '()))))))) | |
| | |
| and so on. | |
|# | |
(import (ice-9 match)) ;; pattern matching is implemented in a library in Guile | |
#| Syntactic sugar/obfuscation |# | |
(define : cons) | |
(define :ΒΉ car) | |
(define :Β² cdr) | |
(define :Β² cdr) | |
(define =? eq?) | |
#| Second/cdr-biased functor map for cons cells |# | |
(define (:-map π ΒΉ-Β²) (: (:ΒΉ ΒΉ-Β²) (π (:Β² ΒΉ-Β²)))) | |
#| Anamorphism for streams. Conceptual type signature: | |
| unfold :: (b -> Maybe (a, b)) -> b -> Stream a | |
|# | |
(define (β π π₯) | |
(delay (match (π π₯) | |
(#f '()) | |
((π₯ . π₯') (: π₯ (β π π₯')))))) | |
#| Functor map for a stream |# | |
(define (β-map π π₯π₯) | |
(delay (match (force π₯π₯) | |
(() '()) | |
((π₯ . π₯π₯') (: (π π₯) (β-map π π₯π₯')))))) | |
#| Drop a given number of elements from the beginning of stream |# | |
(define (β i π₯π₯) | |
(if (=? i 0) | |
π₯π₯ | |
(β (- i 1) (:Β² (force π₯π₯))))) | |
#| Pluck out an element from a stream, if you can find one that matches the | |
| predicate. | |
|# | |
(define (? ?' π₯π₯) | |
(match (force π₯π₯) | |
((π₯ . π₯π₯') (if (?' π₯) π₯ (? ?' π₯π₯'))))) | |
#| Zip two streams into one, ending when either ends, if either does |# | |
(define (=>- π₯π₯ π¦π¦) | |
(delay (match (: (force π₯π₯) (force π¦π¦)) | |
((() . _) '()) | |
((_ . ()) '()) | |
(((π₯ . π₯π₯') . (π¦ . π¦π¦')) (: (: π₯ π¦) (=>- π₯π₯' π¦π¦')))))) | |
#| Average (mean) of two numbers |# | |
(define (avg π₯ π¦) (/ (+ π₯ π¦) 2)) | |
#| One iteration of the square-root approximation method |# | |
(define (ββ β π₯) (avg β (/ π₯ β))) | |
#| Comonad duplicate for cons cells |# | |
(define (:-dup π₯) (: (:ΒΉ π₯) π₯)) | |
#| Apply a cons cell to a function |# | |
(define (ap-: π) (Ξ» (π₯-π¦) (π (:ΒΉ π₯-π¦) (:Β² π₯-π¦)))) | |
#| ββ adapted for use with β |# | |
(define (βββ β π₯) (:-dup (: (ββ β π₯) π₯))) | |
#| The square root algorithm considered as a function of iterations as the | |
| number of iterations approaches infinity, encoded as an infinite stream of | |
| approximations. | |
|# | |
(define (βββ π₯) (β (ap-: βββ) (: 1.0 π₯))) | |
#| Function composition operator |# | |
(define (β π π°) (Ξ» (π₯) (π (π° π₯)))) | |
#| S-combinator from SKI calculus |# | |
(define (π π π° π₯) (π π₯ (π° π₯))) | |
#| The idea of being good enough, in the abstract, is being close to some | |
| tolerance. What this tolerance is, and what we are comparing it to, we don't | |
| specify here. Booyeah orthogonality. | |
|# | |
(define (Β±? Β±) (Ξ» (π₯) (> Β± (abs π₯)))) | |
#| Take a derivative, in the sense of derivative calculus, of a function | |
| represented as a stream. | |
|# | |
(define (β π) (β-map (ap-: -) (=>- (β 1 π) π))) | |
#| Find an approximation for the square root of π₯ when the our answer starts to | |
| change less than the given tolerance. Example: | |
| > (β 0.0000000001 2) | |
| 1.4142135623746899 | |
|# | |
(define (β Β± π₯) (:ΒΉ (? (β (Β±? Β±) :Β²) (π =>- β (βββ π₯))))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment