From 25c4b7a7796ad6663981ddb3a82c19b9e4e41be9 Mon Sep 17 00:00:00 2001 From: Jakob Meier Date: Sun, 21 May 2023 16:15:11 +0200 Subject: [PATCH] Started working on an article about functional programming currently delayed by problems with syntax highlighting, when using emacs --batch --- src/feeds/coding/functional-programming.org | 322 ++++++++++++++++++++ 1 file changed, 322 insertions(+) create mode 100644 src/feeds/coding/functional-programming.org diff --git a/src/feeds/coding/functional-programming.org b/src/feeds/coding/functional-programming.org new file mode 100644 index 0000000..5ee8be0 --- /dev/null +++ b/src/feeds/coding/functional-programming.org @@ -0,0 +1,322 @@ +#+TITLE: Let's check out functional programming +#+DESCRIPTION: (functional (programming)) +#+KEYWORDS: functional-programming, lisp, math +#+SETUPFILE: ../../config.org +#+OPTIONS: toc:nil num:nil +#+DATE: <2023-02-16 Thu 17:00> + +* What is functional programming +According to [[https://wikipediaorg/wiki/Functional_programming?lang=en][Wikipedia]], +functional programming is defined as +#+begin_quote +It is a declarative programming paradigm +in which function definitions are trees of expressions +that map values to other values, +rather than a sequence of imperative statements +which update the running state of the program. +#+end_quote + +This essentially means, +that there are no variables with a state, +rather, you pass through values as function arguments +and changing them can only be accomplished by running a function, +and using its return value. + +Lisp in itself has a lot of functional components, +which is why we'll be using it in the following chapters, +in which we'll try to build a basic math library. + +* Rules +First of all, let's assume we have access to the following three functions, +and three logic operators: (~not~, ~and~, ~or~) +#+NAME: Successor & Predecessor +#+CAPTION: Successor & Predecessor +#+begin_src elisp :exports both :results none +(defun suc (x) (+ x 1)) +(defun pred (x) (- x 1)) +(defun is-zero (x) (= x 0)) +#+end_src + +These three operations allow us +to implement most of the basic algebra operations. +Before we take a look at them, though, +let's write a function that allows us to compare to numbers. + +* Basic logic functions +#+NAME: Comparisons +#+CAPTION: Comparisons +#+begin_src elisp :exports both :results none +(defun same (x y) + (if (is-zero x) (is-zero y) + (same (pred x) (pred y)))) +#+end_src + +The three operations also allow us to define both ~odd~ and ~even~ +without the need for the ~modulo~ operator. +#+NAME: Even & Odd +#+CAPTION: Even & Odd +#+begin_src elisp :exports both :results scalar +(defun odd (x) + (if (is-zero x) nil + (even (pred x)))) +(defun even (x) + (if (is-zero x) t + (odd (pred x)))) + +(mapcar '(lambda (x) + (if (even x) + "Even" + "Odd")) + '(1 2 3 4 5 6)) +#+end_src +#+RESULTS: Even & Odd +: ("Odd" "Even" "Odd" "Even" "Odd" "Even") + +* Basic Arithmetic + +We are able to define an ~add~ function, +which is able to add two natural numbers. +Notice how we are using the ~same~ function which we defined earlier. +#+NAME: Addition +#+CAPTION: Addition +#+begin_src elisp :exports both +(defun suc (x) (+ x 1)) +(defun add3 (x y z) + (if (same y z) x + (add3 (suc x) y (suc z)))) +(defun add (x y) + (add3 x y 0)) + +(print (add 3 5)) +#+end_src +#+RESULTS: Addition +: 8 + +Similarly to how we defined addition, +we can define subtraction. +But instead of using the /z/ variable to count up, +we use it to count down. +That way we do not need the ~same~ function. +#+NAME: Subtraction +#+CAPTION: Subtraction +#+begin_src elisp :exports both +(defun sub3 (x y z) + (if (is-zero z) x + (sub3 (pred x) y (pred z)))) +(defun sub (x y) + (sub3 x y y)) + +(print (sub 3 5)) +#+end_src +#+RESULTS: Subtraction +: -2 + +Because we are counting down towards zero, +we do no longer need the ~sub3~ helper function. +#+NAME: Subtraction 2 +#+begin_src elisp :exports both +(defun sub2 (x y) + (if (is-zero y) x + (sub2 (pred x) (pred y)))) + +(print (sub2 3 5)) +#+end_src +#+RESULTS: Subtraction 2 +: -2 + +The same principle applies to the Addition as well +#+NAME: Addition 2 +#+begin_src elisp :exports both +(defun add2 (x y) + (if (is-zero y) x + (add2 (suc x) (pred y)))) +(print (add2 3 5)) +#+end_src +#+RESULTS: Addition 2 +: 8 + +As you probably know, +multiplying two numbers (~x * y~) is the same as adding /x/, +to itself /y/ times. +We can do the same thing with recursion. +#+NAME: Multiplication +#+CAPTION: Multiplication +#+begin_src elisp :exports both +(defun mult (x y) + (if (is-zero y) 0 + (add2 x (mult x (pred y))))) + +(print (mult 2 5)) +#+end_src +#+RESULTS: Multiplication +: 10 + +By using the ~mult~ function, we just created, +we can now define ~pow~. +#+NAME: Power +#+CAPTION: Power +#+begin_src elisp :exports both +(defun pow (x y) + (if (is-zero y) 1 + (mult x (pow x (pred y))))) + +(pow 2 3) +#+end_src +#+RESULTS: Power +: 8 + +* Division + +That's the easy functions done. +Division is a little bit harder, +because we do not want to use floating point numbers. +Luckily, we can define our division to be of the format: +#+begin_example +x = q * y + r +#+end_example +Where q is the factor (sometimes known as ~//~) +and r is the remainder (also known as ~mod~ or ~%~). + +Unfortunately, this requires us to have access to +a /less-than/ or /greater-than/ comparator. +By definition, we aren't allowed to use them, though, +which means that we have to build them ourselves. + +** Advanced Comparators + +We can achieve this by subtracting /y/ from /x/ +and increasing the value until we reach /x/. +That way we know if it less, +by checking if we go past /0/ whilst incrementing. +#+NAME: Less +#+begin_src elisp :exports both +(defun less2 (x z) + (cond + ((is-zero z) t) + ((same x z) nil) + (t (less2 x (suc z)) + ))) +(defun less (x y) + (if (same x y) nil + (less2 x (- x y)))) + +(print (if (less 8 5) + "Less" + "More or equal")) +#+end_src +#+RESULTS: Less +: More or equal + +Using ~less~ we are able to construct a basic ~more~ function. +We do not even need a ~not~ operator, +because we can simply swap the /x/ and /y/ values. +#+NAME: More +#+begin_src elisp :exports both +(defun more (x y) + (less y x)) + +(print (if (more 8 5) + "More" + "Less or equal")) +#+end_src +#+RESULTS: More +: More + +** Divide it! +Now that we are able to compare two values properly, +we can finally implement the division. +#+NAME: Division +#+CAPTION: Division +#+begin_src elisp :exports both +(defun ddiv (x y) + (if (less x y) 0 + (suc (ddiv (sub x y) y)))) +(print (ddiv 7 3)) +#+end_src +#+RESULTS: Division +: 2 + +By using the same method, +but returning the remainder instead of the factor /q/, +we can define the ~mod~ function +#+NAME: Remainder +#+begin_src elisp :exports both +(defun rrem (x y) + (if (less x y) x + (rrem (sub x y) y))) +(print (rrem 7 3)) +#+end_src +#+RESULTS: Remainder +: 1 + +Using all of these functions, we build so far, +we can calculate the fibonacci numbers: +#+NAME: Fib +#+CAPTION: Fibonacci +#+begin_src elisp :exports both +(defun fib (n) + (cond + ((same n 0) 1) + ((same n 1) 1) + (t (add (fib (pred n)) (fib (pred (pred n))))))) + +(print (fib 4)) +#+end_src +#+RESULTS: Fib +: 5 + +Now that we are able to divide two natural numbers, +we can do some more advanced calculations. +This also allows us to speed up two of the functions, +we wrote previously. + +Let's begin with the multiplication +#+NAME: Multiplication (efficient) +#+begin_src elisp :exports both +(defun mult2 (x y) + (cond + ((is-zero y) 0) + ((even y) + (mult2 (add x x) (ddiv y 2))) + (t (add x (mult2 x (pred y)))))) +(mult2 2 3) +#+end_src +#+RESULTS: Multiplication (efficient) +: 6 + +We can also do better than our current ~pow~ function. +#+NAME: Power (efficient) +#+begin_src elisp :exports both +(defun pow2 (x y) + (cond + ((is-zero y) 1) + ((even y) + (pow (mult2 x x) (ddiv y 2))) + (t (mult2 x (pow x (pred y)))))) + +(pow2 2 6) +#+end_src +#+RESULTS: Power (efficient) +: 64 + +* Closing thoughts +That's as far as I'm willing to take this for now. + +Keep in mind that this only works with natural numbers, +so no negative numbers allowed. + +You might be wondering: =Why functional programming=, +and that is a reasonable question, +as there doesn't appear to be a use beyond basic mathematics. + +I've heard a lot of people say, +that using functional programming languages, +expands your way of thinking about problems +and thus improves problem-solving skills. +I guess it is your task to judge that. + +Actually, +functionally programming languages, +like [[https://elixir-lang.org/][elixir]] or [[https://www.haskell.org/][Haskell]] are still being used nowadays, +for example, a fairly popular [[https://www.w3.org/TR/activitypub/][ActivityPub]] server +called [[https://git.pleroma.social/pleroma/pleroma][pleroma]] is written in elixir. -- 2.38.5