guile.org 62 KB

#+TITLE: Emacs Lisp Cheat Sheet #+AUTHOR: Joshua Branson # make sure that we can see the text #+LATEX_HEADER: \usepackage{lmodern} #+LATEX_HEADER: \usepackage[QX]{fontenc} # #+STARTUP: latexpreview * Introduction Guile is a scheme based lisp that is intended to be the universal GNU extension language. It is designed to trivially inter-operate with C code (though I wish it could do the same with Rust code), and its compiler supports programming in other languages (javascript, emacs-lisp, and hopefully someday many others). Guile has a module system, threads, full access to POSIX calls, dynamic linking, networking support, string processing, a foreign function call interface (let's Guile run a function from a different language), etc. When emacs-lisp became so popular in extending Emacs, some of the leaders of the GNU project, wanted to make an official extension language for all others GNU packages. Currently only a few Emacs packages use Guile as an extension language, but hopefully someday many more will. In fact, perhaps in a few years, the default Emacs package will probably run on the Guile vm (running emacs lisp code), which will make the default Emacs experience that much faster! Trying to write code in a low level language like C, is notoriously difficult. Guile makes programming easy and fun! If you are interesting in learning to code, then GNU Guile is a good language to use. * Learning Some Scheme ** Absolute Basic of Scheme Scheme, like most lisps, has a very limited syntax. This is actually a valuable language feature, because it makes the language easy to learn. The two main things to learn are the following: - ~(operator operand1 operand2 operand3 ...)~ The first word in a parenthesis in guile, is a command. The following words are arguments to the command. For example, ~(+ 5 3)~ evaluates to 8. This calculation commands can even be nested in complicated ways like #+BEGIN_SRC scheme (+ 5 (* 3 (- 8 3 (- 10 9)) (/ 10 2)) (+ 8 9 10)) #+END_SRC which can be rearranged to be so that humans can understand how to compute this odd arbitrary expression. #+BEGIN_SRC scheme (+ 5 (* 3 (- 8 3 (- 10 9)) (/ 10 2)) (+ 8 9 10)) #+END_SRC This set up is quite nice. If you align the operands vertically it gives one a good understanding up what is happening. ** Scheme is latently typed Scheme is latent-ly typed. This is like dynamic typing, which is like python. A variable does not have to have an associated type with it. The following python code demonstrates this. The variable ~a~ can change between a string or a number depending on what is assigned to it. #+BEGIN_SRC python :results output :export both a = 5 print (a) a = "hello" print (a) #+END_SRC #+RESULTS: : 5 : hello ** Defining variables Defining variables in scheme is easy too. #+BEGIN_SRC scheme (define color blue) #+END_SRC To change a variable's value, one does #+BEGIN_SRC scheme (set! x blue) #+END_SRC In scheme ~#t~ and ~#f~ are the booleans true and false. You can also easily define functions #+BEGIN_SRC scheme (define *3 (lambda (x) (* x 3))) #+END_SRC You can also write it like: #+BEGIN_SRC scheme (define (*3 x) (* x 3)) #+END_SRC ** Going back to our lisp roots Scheme is a dialect of lisp, which stands for list processing. A list is just a bunch of linked cons cells. A cons cell is a constructor that makes lists. A cons cell contains two element. The first element is called the car, and the second is called tho cdr (could-er). These are cons cells. #+BEGIN_SRC emacs-lisp (cons 1 2) #+END_SRC #+RESULTS: : (1 . 2) #+begin_src emacs-lisp :tangle yes '(1 2) #+end_src #+RESULTS: | 1 | 2 | #+BEGIN_SRC scheme (cons 1 '()) #+END_SRC #+RESULTS: : (1) To make a list, you build up a ton of cons cells: #+BEGIN_SRC scheme (cons 5 (cons 2 (cons 6 (cons 1 '())))) #+END_SRC #+RESULTS: : (5 2 6 1) You can also make a list like this: #+BEGIN_SRC scheme '(5 . (2 . (4 . ( 8 . ())))) #+END_SRC #+RESULTS: : (5 2 4 8) Since scheme processes lists, you can write a plus operation as a list. #+BEGIN_SRC scheme (+ . (5 . (2 . ()))) #+END_SRC #+RESULTS: : 7 There are also some things that look like procedures, but are actually special syntax. A good example is the "if" syntax. #+BEGIN_SRC scheme (if (string=? (read-answer "should I copy this file") "yes") (copy-file "/path/to/file.scm") (print "Fine I won't copy the file.")) #+END_SRC If "if" was a procedure, then copy-file and string=? would be evaluated, then given to if as arguments. BUT that's not what happens. Instead, if acts in a different way. Namely if TEST THEN ELSE. If TEST returns true, then the THEN is executed. Otherwise the ELSE is executed. ** Quoting The first word after the parenthesis is the procedure and the other words are it's arguments. #+BEGIN_SRC scheme (+ 2 3) #+END_SRC #+RESULTS: : 5 But you can quote something and it just returns data #+BEGIN_SRC scheme '(+ 2 3) #+END_SRC #+RESULTS: : (+ 2 3) You can also quote things and quasi-quote in the middle. #+BEGIN_SRC scheme `(+ 2 ,(* 2 4)) #+END_SRC #+RESULTS: : (+ 2 8) You can also unquote slicing! #+BEGIN_SRC scheme (define x '(2 3)) `(1 ,x 4) #+END_SRC #+RESULTS: | 1 | (2 3) | 4 | #+BEGIN_SRC scheme (define x '(2 3)) `(1 ,@x 4) #+END_SRC #+RESULTS: | 1 | 2 | 3 | 4 | #+BEGIN_SRC scheme (define x '(2 3)) `(1 (+ ,@x) 4) #+END_SRC #+RESULTS: | 1 | (+ 2 3) | 4 | ** Closures Closures are a bit like static variables in C. It is a way to make a function being called repeatedly remember the value of the variables instead of resetting them. ie: #+BEGIN_SRC scheme (let loop ((number 0)) (lambda () (unless (> number 10) (set! number (+ 1 number)) (display "Number is ") (display number) (display "\n") (loop number)))) #+END_SRC #+RESULTS: : #:6:2 ()> ** atoms This is an atom #+BEGIN_SRC scheme atom #+END_SRC #+RESULTS: : (atom) So is this #+BEGIN_SRC scheme stnahuntsuhntahouetnhsn34thu #+END_SRC So is this #+BEGIN_SRC scheme 23435345 #+END_SRC I'm not an atom, I'm a list #+BEGIN_SRC scheme '() #+END_SRC #+RESULTS: : () Guile doesn't have the standard scheme procedure to check for atoms: "atom?". Let's write one! #+NAME: atom #+BEGIN_SRC scheme :noweb yes (define atom? (lambda (a) (if (list? a) #f #t))) #+END_SRC ** S-expressions All atoms are S-expressions This is an S-expression #+BEGIN_SRC scheme atom #+END_SRC ** lists A list is a set of S expressions enclosed by parenthesis #+BEGIN_SRC scheme '(I'm a list) #+END_SRC #+RESULTS: : (I'm a list) I'm a list #+BEGIN_SRC scheme '() #+END_SRC ** null? Null returns true if it's parameter is an empty list. #+BEGIN_SRC scheme (null? '()) #+END_SRC #+RESULTS: : #t #+BEGIN_SRC scheme (null? 0) #+END_SRC #+RESULTS: : #f #+BEGIN_SRC scheme (null? "") #+END_SRC #+RESULTS: : #f ** car Get the first item in the list #+BEGIN_SRC scheme (car '(a b c d)) #+END_SRC #+RESULTS: : a #+BEGIN_SRC scheme (car '((hello dog) ((Yup this is cool)) 5 o e)) #+END_SRC #+RESULTS: : (hello dog) #+BEGIN_SRC scheme (car (quote (a b c d))) #+END_SRC #+RESULTS: : a Car is only defined for non-empty lists. #+BEGIN_SRC scheme (car '()) #+END_SRC #+RESULTS: : "An error occurred." ** cdr cdr is pronounced "could-er". cdr is the list without the first item. #+BEGIN_SRC scheme (cdr '(a b c d)) #+END_SRC #+RESULTS: : (b c d) #+BEGIN_SRC scheme (cdr '(((vegan hot dog)) bc d (5 4 3))) #+END_SRC #+RESULTS: : (bc d (5 4 3)) #+BEGIN_SRC scheme (cdr (quote (a b c d))) #+END_SRC #+RESULTS: : (b c d) cdr is defined for non-empty lists. #+BEGIN_SRC scheme (cdr '()) #+END_SRC #+RESULTS: : "An error occurred." ** eq? eq? takes two arguments and each must be a non-numeric atom or a list. #+BEGIN_SRC scheme (eq? 5 5) #+END_SRC #+RESULTS: : #t #+BEGIN_SRC scheme (eq? (car (quote (a b c))) (quote a)) #+END_SRC #+RESULTS: : #t The above can be written like #+BEGIN_SRC scheme (eq? (car '(a b c)) 'a) #+END_SRC #+RESULTS: : #t #+BEGIN_SRC scheme (eq? (cdr '(a b c d)) '(b c d)) #+END_SRC #+RESULTS: : #t ** lat? lat? asks is the list is made up of only atoms. Guile doesn't have it apparently. #+NAME: lat #+BEGIN_SRC scheme :noweb yes <> (define lat? (lambda (l) (cond ;; is l '() ? ((null? l) #t) ;; is the first item in the list an atom? ((atom? (car l) (lat? (cdr l)))) ;; if (car l) is not an item, then return false. (else #f)))) #+END_SRC #+RESULTS: lat : # #+BEGIN_SRC scheme <> (display (lat? '(a b c d e))) #+END_SRC #+RESULTS: ** The cons constructor cons stands for constructor. It constructs lists. This first example is an improper list. Operations like map and filter will not work on them. #+BEGIN_SRC scheme (cons '1 '2) #+END_SRC #+RESULTS: : (1 . 2) #+BEGIN_SRC scheme (cons '1 (cons '2 '())) #+END_SRC #+RESULTS: : (1 2) #+BEGIN_SRC scheme (cons '1 (cons '2 (cons '3 (cons '4 (cons '5 '()))))) #+END_SRC #+RESULTS: : (1 2 3 4 5) You can also just use list, which is much easier. #+BEGIN_SRC scheme (list 1 2 3 4 5) #+END_SRC #+RESULTS: : (1 2 3 4 5) ** (read-answer) is a cool way to get user input ** defines/cond Let's define an abs function or absolute value function #+BEGIN_SRC scheme (define (abs x) (cond ((> x 0) x) ((< x 0) (- x)) ((= x 0) 0))) #+END_SRC There is a simpler solution though. If x is greater than 0, return x. Otherwise return -x. #+BEGIN_SRC scheme (define (abs x) (cond ((> x 0) x) (else (- x)))) #+END_SRC ** no for or while loops? named let or recursion Schemers frown upon using traditional programming loops. Since scheme is a mostly functional programming language, it discourages using loops. Instead it encourgaes you to write recursive functions (which normally are shorter than for loops). Most schemes then compile/interpret those recursive calls into for loops internally anyway. This is called tail call optimization. #+BEGIN_SRC scheme (define (print1-10 x) (unless (= x 11) (display x) (display " ") (print1-10 (+ x 1)))) (print1-10 1) #+END_SRC This can be done a little better though. You can used a named let. It names a function and then calls it. #+BEGIN_SRC scheme (let loop ((x 1)) (unless (= x 11) (display x) (display " ") (loop (+ 1 x)))) #+END_SRC This is the same as #+BEGIN_SRC scheme (define loop (lambda (x 1) (unless (= x 11) (display x) (display " ") (loop (+ 1 x))))) (loop) #+END_SRC *** Looping through loops the basic structure The little scheme recommends this structure for looping through loops or building a list. First you ask if the list is ~null?~, then you ~cons~ the first element with the ~(func (cdr list))~. #+BEGIN_SRC scheme (define numbers (list "hello " "world " "!\n")) (define (length-of-list list) (cond [(null? list) 0] [(+ 1 (length-of-list (cdr list)))])) #+END_SRC #+BEGIN_SRC scheme (define (string-in-list? string list) (cond [(null? list) #f] [(string=? string (car list)) #t] [else (string-in-list? string (cdr list))])) #+END_SRC ** syntactic sugar for lists (srfi srfi-1) Scheme's essential data type is a list. You can use named lets or usual recursion to loop through those lists, but you can also use ~map~, ~fold~, ~apply~, ~eval~, or ~unfold~. *** map is for building lists, which is better than recursion or named lets. ~map~ lets you apply a function to a list, and it returns a list. Take a look, which function is shorter? #+BEGIN_SRC scheme (define (string-in-list? string list) (cond [(null? list) #f] [(string=? string (car list)) #t] [else (string-in-list? string (cdr list))])) (define (string-in-list? string list) (primitive-eval (cons 'or (map (lambda (var) (string=? string var)) list)))) #+END_SRC How about this one? #+BEGIN_SRC scheme (define (contains-duplicate? list) (if (null? list) #f (or ;; (let loop ([list (cdr list)] [1st (car list)]) (if (null? list) #f (if (equal? 1st (car list)) (values #t 1st) (loop (cdr list) 1st)))) ;; (contains-duplicate? (cdr list))))) (define (contains-duplicate? list) (if (null? list) #f (or (primitive-eval (cons 'or ; check if (car list) is in (cdr list) (map (lambda (var) (equal? var (car list))) (cdr list)))) ;; check if (cdr list) contains duplicate (contains-duplicate? (cdr list))))) #+END_SRC Unfortunately you cannot directly map using ~or~, because or is not a function: it is a macro. That's why the above uses ~primitive-eval~. *** fold is for combining lists or constructing lists. #+BEGIN_SRC scheme :results raw :exports both (define (reverse-list list) (fold cons '() list)) #+END_SRC #+RESULTS: ** wait [] are the same as ()? Apparently, scheme treats [] the same as (). This can be useful in let expressions to make them easier to read. #+BEGIN_SRC scheme (let ([n 5] [b 4]) (+ n b)) #+END_SRC #+RESULTS: : 9 I also like using brackets for ~cond~ #+BEGIN_SRC scheme (define var "hello") (cond [(string? var) (display var)] [(number? var) (display (number->string))] [else (display "nada")]) #+END_SRC Check this out, you can store procedures in a let variable: #+BEGIN_SRC scheme (let ([a +]) (a 1 2 3)) #+END_SRC #+RESULTS: : 6 ** lambdas Scheme functions are called procedures, because scheme is based on lambda calculus. Often times procedures are created using a lambda expression. #+BEGIN_SRC scheme (lambda (x y z) (+ x y z)) #+END_SRC Here is what is really cool! You can create a lambda function and immediately pass it arguments. This is like an anonymous function in javascript. #+BEGIN_SRC scheme ((lambda (x y z) (+ x y z)) 1 2 3) #+END_SRC #+RESULTS: : 6 ** create non-global variables let again #+BEGIN_SRC scheme (let ((a 1) (b 2)) (+ a b)) #+END_SRC ** what's let* ? (let* binds the variable values sequentially. Suppose you have a let like so: #+BEGIN_SRC scheme (let ((a 1) (b (+ 1 a))) (+ a b)) #+END_SRC #+RESULTS: : "An error occurred." The error occurs because scheme tries to define both a & b at the same time! This won't work, because b can only be defined after a has a value. So instead, you must use let* #+BEGIN_SRC scheme (let* ((a 1) (b (+ 1 a))) (+ a b)) #+END_SRC #+RESULTS: : 3 ** What's letrec ? #+BEGIN_SRC scheme (letrec ((even? (lambda (n) (if (zero? n) #t (odd? (- n 1))))) (odd? (lambda (n) (if (zero? n) #f (even? (- n 1)))))) (even? 88)) #+END_SRC ** closures A closure is an environment with the local variables and a reference to the higher environment, which contains a reference to all of the higher environments' variables. One create a new sub-environment, when one uses a lambda. The previous environment is saved between calls. This is called a closure. #+BEGIN_SRC scheme (define make-counter () (let ((current-number 0)) (lambda () (set! current-number (+ current-number 1)) current-number))) (define number-generator (make-counter)) #+END_SRC Each call to number-generator, will display an increasing number... A better example is: #+BEGIN_SRC scheme (let loop ((x i)) (display x) (loop (1+ x))) #+END_SRC ** date types *** Vectors #+BEGIN_SRC scheme #(1 2 3) #+END_SRC #+RESULTS: : #(1 2 3) *** lists A list is built up of cons: #+BEGIN_SRC scheme (cons 1 (cons 2 (cons 3 (cons 4 '())))) #+END_SRC #+RESULTS: : (1 2 3 4) #+BEGIN_SRC scheme (list 1 2 3 4 5 6 7 8) #+END_SRC #+RESULTS: : (1 2 3 4 5 6 7 8) This is in improper list #+BEGIN_SRC scheme (cons 1 (cons 2 (cons 3 4))) #+END_SRC #+RESULTS: : (1 2 3 . 4) *** pairs #+BEGIN_SRC scheme (cons 1 2) #+END_SRC #+RESULTS: : (1 . 2) ** Scheme conventions A function ending in "?" returns a boolean value. A function ending in "!" is a function or a macro with side effects. ** conditionals *** if (if TEST THEN ELSE) #+BEGIN_SRC scheme (if (= 5 5) #t #f) #+END_SRC #+RESULTS: : #t The coolest if I've seen. #+BEGIN_SRC scheme ((if #f + *) 3 4) #+END_SRC *** cond (cond ...) Each clause looks like ( ...) #+BEGIN_SRC scheme :hlines yes (cond ((< 4 1) #f) ((= 6 7) #f) (else #t)) #+END_SRC #+RESULTS: : #t Clauses can also be written as (test => ) #+BEGIN_SRC scheme :hlines yes (cond ((< 4 1) => #f) ((= 6 7) => #f) (else #t)) #+END_SRC #+RESULTS: : "An error occurred." *** case (case ...) Each clause looks like: (( ...) ...) The last clause can look like (else ) #+BEGIN_SRC scheme (case (* 2 3) ((2 3 5 7) 'prime) ((1 4 6 8) 'composite) (else 'whoKnows)) #+END_SRC #+RESULTS: : composite ** begin Sequentially evaluate all the expressions left to right, and return the last result. (begin ...) ** keywords Suppose I have a function that has 10 optional arguments. It would be silly to force the user of the function remember each argument. Instead the person can use keywords. ie: These two function calls are the same: #+BEGIN_SRC scheme (make-window #:width 800 #:height 100 #:color green #:font Arial) (make-window #:height 100 #:width 800 #:font Arial #:color green) #+END_SRC * TODO :LOGBOOK: - State "TODO" from [2021-01-24 Sun 14:42] :END: ** I can try to implement gnulib with guile and that'll help guile's portability issues. leoprikler mentioned that guile has scheme code that implements file-is-directory? When POSIX is not defined, it does a weird hack. Gnulib probably has a more portable way to do this. leoprikler: the difficulty with a gnulib-only approach is that gnulib doesn't implement windows versions of posix calls that don't map 100%. So instead of having approximate versions of 'ttyname' or 'getuid' or a dozen other things, gnulib doesn't bother. So Guile on Win32 just doesn't define a bunch of posix calls. And thus the test suite is full of '(if (defined? '...)' special cases [15:17] well, perhaps having an approximate getuid is asking a bit much, but an approximate file-is-directory? should be possible [15:19] (implemented through stat on linux and through something else on windoof) ** install gitile to let people browse my source repos https://git.lepiller.eu/gitile ** integrate this online guide about how to set up autotools with a simple guile program with the guile hacker handbook https://erikedrosa.com/2017/10/29/guile-projects-with-autotools.html CC By SA :justInCaseTheOnlineGuideDisappearsHereIsACopy: erikedrosa.com Setting up a GNU Guile project with Autotools Erik Edrosa 15-19 minutes Lisp, specifically Scheme, has captivated me for about three years now. My Scheme implementation of choice is GNU Guile because it is the official extension language of the GNU project. One issue I faced early with when trying to develop for Guile was how to set up and organize my project. There doesn't seem to be a single recommended way to set up a Guile project but several projects do follow a similar project structure I will describe below. You can find a git repository with the example project here. Simple Project Structure The project template I created for new projects is based on several GNU Guile projects I have examined. These projects follow the traditional GNU Build System using the familiar commands ./configure && make && sudo make install for building and installing software. To help generate these files, we will use the collection of software known as autotools which include the software Autoconf and Automake. Unfortunately, autotools can be quite complex for developers with its esoteric languages like m4 being used to magically create and configure all the necessary build files for your project. Good news for us, not much magic is needed for us to conjure the build files for a simple Guile project. . ├── bootstrap ├── configure.ac ├── COPYING ├── COPYING.LESSER ├── guile.am ├── m4 │ └── guile.m4 ├── Makefile.am ├── skeleton │ └── hello.scm ├── pre-inst-env.in ├── README └── skeleton.scm Above is the directory structure of the project. bootstrap is a simple shell script which a developer can regenerate all the GNU Build System files. configure.ac is a template file which Autoconf uses to generate the familiar configure script. m4/guile.m4 is a recent copy of Guile's m4 macros, may not be needed if you prefer to use the macro from your Guile distribution, but it is recommended to keep your own copy. COPYING and COPYING.LESSER are just the GPL and LGPL licenses. Makefile.am and guile.am are Automake files used to generate the Makefile.in which configure will configure. skeleton.scm and skeleton/hello.scm are some initial source code files, where skeleton.scm represents the Guile module (skeleton) and skeleton/hello.scm is the (skeleton hello) module, change these file and directory names to what you want to name your modules as. pre-inst-env.in is a shell script which set up environment variables to be able to use your code before installing it. Bootstrapping the Project #! /bin/sh autoreconf --verbose --install --force This is the bootstrap script, it just calls autoreconf, which uses Autoconf and Automake, to generate the configure script from configure.ac and Makefile.in file from Makefile.am. The bootstrap script is sometimes also named autogen.sh in projects but seems to no longer be preferred to avoid confusion with the GNU AutoGen project. The command will also generate a bunch of other files needed by the build process. This script is only used when building from a checkout of the project's repository, because a user will only need configure and Makefile.in. Whenever you might be having an issue with the configure script or made a change to it, doing ./bootstrap will regenerate the files for you. Generating the Configure Script AC_INIT([guile-skeleton], [0.1]) AC_CONFIG_SRCDIR([skeleton.scm]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_MACRO_DIR([m4]) AM_INIT_AUTOMAKE([-Wall -Werror foreign]) GUILE_PKG([2.2 2.0]) GUILE_PROGS if test "x$GUILD" = "x"; then AC_MSG_ERROR(['guild' binary not found; please check your guile-2.x installation.]) fi AC_CONFIG_FILES([Makefile]) AC_CONFIG_FILES([pre-inst-env], [chmod +x pre-inst-env]) AC_OUTPUT Above is the configure.ac file used by GNU Autoconf to generate the configure script. The first line is the AC_INIT macro, the first argument is the package name and the second argument is the version. There is a couple of other optional arguments which you can learn more about here. The AC_CONFIG_SRCDIR macro adds a check to configure for the existence of a unique file in the source directory, useful as a safety check to make sure a user is configuring the correct project. AC_CONFIG_AUX_DIR macro is where auxiliary builds tools are found, build-aux is the the most commonly used directory, we use this so we don't litter the source directory with build tools. The next macro, AC_CONFIG_MACRO_DIR is where additional macros can be found, we add this to include the m4/guile.m4 file. GNU Automake options are part of the next macro, AM_INIT_AUTOMAKE, where -Wall turns all warnings, -Werror turns those warnings into errors, and finally foreign will turn the strictness to a standard less than the GNU standard. More automake options can be found here. The GUILE_PKG and GUILE_PROGS macro is part of the m4/guile.m4 file. This macro will substitute various variables that will be used in the Makefile. The GUILE_PKG macro will use the pkg-config program to find the development files for Guile and substitute the GUILE_EFFECTIVE_VERSION variable in the Makefile. The GUILE_PROGS macro finds the various Guile programs we will need to compile our program. This macro substitutes the variables GUILE and GUILD with the path to the guile and guild programs. By default, these Guile macros will check for the latest version of Guile first, which is currently 2.2. If you have multiple versions of Guile installed, a user of the configure script may override the above Guile variables. For example if you have Guile 2.2 and 2.0 installed and you want to install the package for 2.0, you can run ./configure GUILE=/path/to/guile2.0. There is also a check to ensure the GUILD variable was set by GUILE_PROGS and displayed an error if it could not be found. The next portion of this file involves the files that will actually be configured when a user runs the configure script. The AC_CONFIG_FILES macro's first argument is files that will be created by substituting the variables found in the file of the same name with .in appended to the end. In the first macro, it creates a Makefile by substituting the variables in Makefile.in. The second argument of this macro will be commands to run after the file is created, in the second macro, it uses chmod to make the pre-inst-env script executable. The last macro in this script is AC_OUTPUT and must be the final macro in configure.ac. This macro generates config.status and then uses it to do all the configuration. Generating the Project Makefile For this project we use GNU Automake to help us generate the Makefile. When Automake is ran, it will produce a Makefile.in file which will then be configured by the configure script. We divide the Makefile into two files, Makefile.am and guile.am. The first file is where we will put code specific for this project, that includes source code and any other files to be distributed to users. guile.am file is where we have all the code that can be shared between any other Guile project. include guile.am SOURCES = \ skeleton/hello.scm \ skeleton.scm EXTRA_DIST = \ README \ bootstrap \ pre-inst-env.in This Makefile.am file is pretty small for now. The Automake script first includes the Guile specific automake script guile.am. The next part is the variable SOURCES which is a list of the project's source code that will be compiled and installed. The next variable, EXTRA_DIST is a list of other files that should be included in the tarball used to distribute this project. moddir=$(datadir)/guile/site/$(GUILE_EFFECTIVE_VERSION) godir=$(libdir)/guile/$(GUILE_EFFECTIVE_VERSION)/site-ccache GOBJECTS = $(SOURCES:%.scm=%.go) nobase_dist_mod_DATA = $(SOURCES) $(NOCOMP_SOURCES) nobase_go_DATA = $(GOBJECTS) # Make sure source files are installed first, so that the mtime of # installed compiled files is greater than that of installed source # files. See # # for details. guile_install_go_files = install-nobase_goDATA $(guile_install_go_files): install-nobase_dist_modDATA CLEANFILES = $(GOBJECTS) GUILE_WARNINGS = -Wunbound-variable -Warity-mismatch -Wformat SUFFIXES = .scm .go .scm.go: $(AM_V_GEN)$(top_builddir)/pre-inst-env $(GUILD) compile $(GUILE_WARNINGS) -o "$@" "$<" Now for guile.am, this file has all of the Guile specific code used in our Automake scripts. The first two variables, moddir and godir, are the paths where we will install our Guile modules and compiled modules. The next variable is the GOBJECTS variable which has some code that creates a list of Guile object files from our SOURCE variable. The next two variables declared are special DATA variables using some of Automake's features to indicate which files should be installed and where. The first portion of the variable, the nobase_ prefix is used to tell Automake to not strip the path of these files when installing them. dist_ tells Automake that these files must be distributed in the tarball. The next part, mod_ or go_, tell which directory these files should be installed, they refer to the above moddir and godir variables. The files in SOURCES and NOCOMP_SOURCES are installed in the moddir, where SOURCES are the scheme files that we want to be compiled and the NOCOMP_SOURCES are scheme files which should not be compiled. The compiled Guile source code, GOBJECTS are installed in the godir. The next two lines of code are some special magic to ensure the files are installed in the right order by Automake. The CLEANFILES variable is an Automake variable with files which should be deleted when a user runs make clean. The compiled Guile modules are just the files we need to delete, so we assign GOBJECTS. GUILE_WARNINGS are warnings we want to pass to Guile when it compiles or executes the code. SUFFIXES allows us to add Guile's .scm and .go file extensions to be handled by Automake and we define a suffix rule on how to compile the source code using GUILD. GNU Guile Project Source Files We now get to the actual Guile code for this project. A Guile project may be divided into several modules and organized in various ways. In this skeleton project, the main module is the skeleton module and is found in skeleton.scm file. Sub-modules of skeleton are found in the skeleton/ directory, where we currently have the skeleton hello module found in the skeleton/hello.scm file. (define-module (skeleton) #:use-module (skeleton hello)) (hello-world) This is the skeleton module, it defines the module with the define-module form. We also import the skeleton hello module using the #:use-module option of define-module. All this file does is call hello-world procedure defined in the skeleton hello module. (define-module (skeleton hello) #:export (hello-world)) (define (hello-world) (display "Hello, World!")) The final module is the skeleton hello module. This module defines the hello-world procedure used in the previous module and then exports it using the #:exports option in the define-module form. Putting It All Together Now how does this all come together for development? With all these files in place in the project, executing the command ./bootstrap will use Autoconf and Automake to generate the configure, Makefile.in, and some other files. Then executing ./configure will configure Makefile.in, pre-inst-env.in. Running the program make should now compile your source code. #!/bin/sh abs_top_srcdir="`cd "@abs_top_srcdir@" > /dev/null; pwd`" abs_top_builddir="`cd "@abs_top_builddir@" > /dev/null; pwd`" GUILE_LOAD_COMPILED_PATH="$abs_top_builddir${GUILE_LOAD_COMPILED_PATH:+:}$GUILE_LOAD_COMPILED_PATH" GUILE_LOAD_PATH="$abs_top_builddir:$abs_top_srcdir${GUILE_LOAD_PATH:+:}:$GUILE_LOAD_PATH" export GUILE_LOAD_COMPILED_PATH GUILE_LOAD_PATH PATH="$abs_top_builddir:$PATH" export PATH exec "$@" Above is the pre-inst-env.in file which is configured by the configure script. The variables between '@' characters are variables that will be replaced by the configure script. abs_top_srcdir and abs_top_builddir are Autoconf variables which gives the absolute source directory and build directory. Then we add these directories to Guile's GUILE_LOAD_COMPILED_PATH and GUILE_LOAD_PATH. GUILE_LOAD_COMPILED_PATH is an environment variable that has the search path for compiled Guile code which have the .go extension. GUILE_LOAD_PATH is the search path for Guile source code files. When the configure script configures this file, it then allows you to run Guile and use the modules of the project before installing them. This can be done with this command ./pre-inst-env guile. The script also does the same for the PATH variable, to allow you to execute any scripts in the project's directory. Finally, the script executes the rest of the command passed into this script. Distributing the Project So the project is now complete and you want to distribute it to other people so they can build and install it. One of the great features of autotools is it generates everything you need to distribute your project. From the Makefile that is generated by GNU Automake, you run make dist and it will generate a tar.gz file of your project. This will be the file you will then give to your users and they will just extract the contents and run ./configure && make && sudo make install to build and install your project. The files that are included in the distribution are figured out by Automake and can be added to in your Makefile.am script file using the EXTRA_DIST variable. One other helpful feature that GNU Automake will generate is the command make distcheck. This command will check to ensure the distribution actually works, it will first create a distribution and then proceed to open the distribution, build the project, run tests, install the project, and uninstall the project all in a temporary directory. You can learn more about GNU Automake distribution in the manual. One more note about installing your GNU Guile project. By default, the GNU Build System installs your project in the /usr/local directory. GNU Guile installations generally do not have this directory on their load path. There are several options on how to resolve this issue. You can add /usr/local/share/guile/site/$(GUILE_EFFECTIVE_VERSION) to the GUILE_LOAD_PATH variable as well as /usr/local/lib/guile/$(GUILE_EFFECTIVE_VERSION)/site-ccache to the GUILE_COMPILED_LOAD_PATH variable, where $(GUILE_EFFECTIVE_VERSION) is the GNU Guile version you are using, like 2.0 or 2.2. You can add these variables to your .profile or .bash_profile in your home directory like so: export GUILE_LOAD_PATH="/usr/local/share/guile/site/2.2${GUILE_LOAD_PATH:+:}$GUILE_LOAD_PATH" export GUILE_LOAD_COMPILED_PATH="/usr/local/lib/guile/2.2/site-ccache${GUILE_LOAD_COMPILED_PATH:+:}$GUILE_COMPILED_LOAD_PATH" The alternative is to install your project in the current load path of your GNU Guile installation which is often /usr. You can easily do this by changing the prefix variable in the configure script like ./configure --prefix=/usr. Now when you run make install it will install everything in /usr instead of /usr/local. With the GNU Build System, you have full control of where you install your Guile files so you have the possibility of installing it anywhere you want like your home directory, just be sure to add that location to your load paths for Guile. There are several other variables you can modify to change the installation location of various files, you can learn more in the GNU Autoconf manual. Conclusion The GNU Build System provides a common interface for configuring, building, and installing software. The autotools project, although a bit complex, helps us achieve this. This should be enough for a basic GNU Guile library that can be compiled and distributed to users using the GNU Build System. You can find the example project on GitLab here. The project can be extended to include tests and documentation that I hope to cover in other blog posts. :END: https://jeko.frama.io/en/index.html https://framagit.org/Jeko/jeko.frama.io ** I can read and learn about automatic memory management: https://gchandbook.org/index.html * POSIX stuff [[info:guile#POSIX][info:guile#POSIX]] * Things I should try to understand ** let, letrec, let* create a local environment with local variables or definitions #+BEGIN_SRC scheme (let ((x 5)) (define (someFunc y) (+ 5 y)) (someFunc x)) #+END_SRC #+RESULTS: : 10 ** delay and force This is how promises are made... (delay ) returns a promise, which can be called by (force) to evaluate string (current-date) "~H:~S")) (newline) #+END_SRC #+RESULTS: Maybe I should implement more srfi modules for guile. * file stuff ** get the current directory #+BEGIN_SRC scheme (getcwd) #+END_SRC #+RESULTS: : "An error occurred." ** change the current directory #+BEGIN_SRC scheme (chdir) #+END_SRC * sending emails with guile The simplest way that I have found to send emails with guile is the following. First you need to install msmtp, which is like sendmail. You'll need to create a configuration file like this: #+BEGIN_SRC sh :results output :exports both cat ~/.msmtprc #+END_SRC #+RESULTS: #+begin_example # Example for a user configuration file ~/.msmtprc # # Set default values for all following accounts. defaults # Use the mail submission port 587 instead of the SMTP port 25. port 587 # Always use TLS. tls on # A dismail service account dismail # Host name of the SMTP server host smtp.dismail.de # Envelope-from address from jbranso@dismail.de # Authentication. The password is given using one of five methods, see below. auth on user jbranso@dismail.de # Password method 3: Store the password directly in this file. Usually it is not # a good idea to store passwords in plain text files. If you do it anyway, at # least make sure that this file can only be read by yourself. password somehardpassword # Set a default account account default : dismail #+end_example #+BEGIN_SRC scheme (define-module easy-email #:use-modules (ice-9 popen) #:export (send-email)) (use-modules (ice-9 popen)) (define (send-email to subject body) (let ((port (open-pipe* OPEN_WRITE "msmtp" to))) (display (email-rfc-2822 subject body) port) (close-pipe port))) (define (email-rfc-2822 subject body) (string-append "Subject: " subject "\n" "Date: Tue, 15 Jun 2021 04:21:06 -0600\n" "Message-ID: <12345@local.machine.example>\n\n" body)) (send-email "jbranso@dismail.de" "joshua@gnucode.me Test Email" "This is another test email for joshua@gnucode.me") #+END_SRC * web modules (use-modules (web uri)) (uri->string (build-uri 'https #:host "www.gnu.org")) ** web client ??? Guile comes with a web client! This is like a headless firefox. It's actually like curl! So here's an example: #+BEGIN_SRC scheme (http-get (string->uri "http://localhost/html/oops.html")) #+END_SRC #+RESULTS: : "An error occurred." This does a get request on that uri! How cool is that! I can use guile to do GET, PUT, POST, etc. ** to build a web application I need to use the response function The way the Web works, is my web server gets a web request. Then my web server sends a web response. So my handler needs to build web responses, via build-response. * security considerations https://savannah.gnu.org/forum/forum.php?forum_id=8705 Modern operating systems cannot guarantee that GETs and POSTs to localhost, come from localhost. AKA if you are running a server on localhost, you have to assume that anyone on the internet can run queries against it. Use named pipes or unix sockets to ensure only local execution only. * cool things to play with guile https://libfive.com/studio/ https://dthompson.us/projects/chickadee.html * writing a guile test to prove that I know guile ** How would you write a function to include an optional argument with keyword "extra"? :Answer: #+BEGIN_SRC scheme (define* (silly #:extra) (when extra #t) #f) #+END_SRC :END: ** What does the following return? #+BEGIN_SRC scheme (define* (cool #:key more) (when more #t #f)) (display (cool #:more #t)) #+END_SRC :answer: #f :END: ** How do you specify the default value for a key in a define* ? :answer: #+BEGIN_SRC scheme (define* (awesome #:key (more 5)) more) (display (awesome)) (display (awesome #:more 3)) #+END_SRC #+RESULTS: : 53 :END: ** write a named let to print the numbers 1-10 :answer: #+BEGIN_SRC scheme (let loop ((i 1)) (when (< i 11) (display i) (display " ") (loop (+ i 1)))) (display "\n") #+END_SRC #+RESULTS: :END: ** What does the following code produce? #+BEGIN_SRC guile (+ 1 #| 1 |# 2) #+END_SRC :answer: 3 guile apparently supports a nested comment! Weird right? #| is the beginning on the commend and #| is the end. #+BEGIN_SRC guile (+ 1 #| 1 |# 2) #+END_SRC #:RESULTS 3 :END: ** How can you test if you can read and write a file? :answer: The access? (path how) procedure. #+BEGIN_SRC guile (access? "~/Downloads/autotools.pdf" 'R_OK) (access? "~/Downloads/autotools.pdf" 'W_OK) ;; or (access? "~/Downloads/autotools.pdf" (logior 'R_OK 'W_OK)) ;; the other ones are X_OK (execute) and F_OK (file exists). #+END_SRC :END: ** write a program to open a file in tmp and write "Hello World!" :answer: #+BEGIN_SRC scheme (use-modules (ice-9 textual-ports)) (let ((port (open-output-file "/tmp/file.txt"))) (put-string port "Hello World!\n") (force-output port)) #+END_SRC #+RESULTS: : # #+BEGIN_SRC sh :results output :exports both cat /tmp/file.txt #+END_SRC #+RESULTS: : Hello World! :END: ** what's the difference between let, letrec, let*, letrec* :answer: let* bindings are performed sequentially. So one variable can be defined in terms of the previous ones #+BEGIN_SRC scheme (let* ((a 2) (b (+ a 1)) (c (+ a b))) ;;code here ) #+END_SRC letrec will let you define a variable with a lambda #+BEGIN_SRC scheme (letrec ((a (lambda () (+ 5 7)))) ;; code ) #+END_SRC letrec* will let you define a variable with a lambda and it defines all of the variables sequentially. #+BEGIN_SRC scheme (letrec* ((a (lambda () (+ 5 3))) (b (lambda () (+ 8 a)))) ;; code here ) #+END_SRC :END: ** what guile module lets you read from stdin? :answer: #+BEGIN_SRC scheme (use-module (ice-9 readline)) #+END_SRC :END: ** what guile module lets you do pattern matching? :answer: (use-module (ice-9 match)) :END: ** how does guile represent a vector? What does it look like when you print it? :answer: #(1 2 3 4) :END: ** What is an alist? :ANSWER: An alist is an association list. It is a list of key and value pairs. Alist = '( (car . cdr) (key . value) ...) :END: ** How do you add an a pair to the association list? :ANSWER: acons (set! task-list (acons 3 "pay gas bill")) The problem with acons is that it will let you have multiple key -> value pairs, where key are not guaranteed to be unique. If you want unique keys, then one should use assq-set!, assv-set!, and assoc-set! :END: ** write some guile macros Before we get into writing scheme macros we should get an idea of what they are. Macros are syntactic transformers of code. In other words, macros are bits of code that expand into code. Macros are a great way to extend the scheme language to solve specific problems, and they offer greater flexibility than writing procedures. *** The pattern language Scheme defines an interesting pattern language to help you match arbitrary bits of code and expand said code. It is illegal for the same pattern variable to appear twice in the same pattern. *** syntax rules Syntax rules are pattern driven macros. If your code matches a pattern, then it will be transformed by a template, which is often extremely helpful! **** Tweaking srfi srfi-9 records The default srfi srfi-9 records define a record like this: define-record-type type (constructor fieldname ...) predicate (fieldname accessor [modifier]) ... Why is the field name mentioned twice? Wouldn't it make more sense to have it look like this: define-record-type type constructor predicate (fieldname accessor [modifier]) ... Here is the scheme macro to do that: #+BEGIN_SRC scheme (use-modules (srfi srfi-9)) (define-syntax my-define-record-type (syntax-rules () ((my-define-record-type type constructor predicate? (fieldname var1) ...) (define-record-type type (constructor fieldname ...) predicate? (fieldname var1) ... )))) #+END_SRC Funnily enough, this macro will let you define a record type that has no fields. like so: #+BEGIN_SRC scheme (my-define-record-type make-dog dog?) #+END_SRC Since, records are ok with no field specifiers, this is ok. **** The limits of syntax rules Syntax rules do not lend themselves to much error reporting. A macro will either match a pattern or not. BUT it more custom and crazy macros could be built, if one could use proper scheme to expand the macros. Consider the ~my-define-record-type~. Suppose that we wanted the macro to expand from this: #+BEGIN_SRC scheme (my-define-record-type make-dog (age dog-age) (breed dog-breed)) #+END_SRC Into this: #+BEGIN_SRC scheme (define-record-type (make-dog age breed) dog? (age dog-age) (breed dog-breed)) #+END_SRC Syntax rules will not do this. Notice that the macro does not include the predicate "dog?", but it expands to include it. With syntax case, one could define the macro such that "", removes the less than/greater than signs and adds a "?" to produce the predicate "dog?". *** Syntax case syntax-case syntax literals (pattern [guard] exp) ... Syntax-case does not provide a syntax transformer, like syntax-rules do. Rather deconstructs syntax objects and returns a different syntax. Syntax-case does not operate on raw lisp forms like defmacros do. That could make it hard for scheme to distinguish between forms that are similar but not in the same position. Instead, scheme uses syntax. You can see this syntax by using the scheme syntax procedure: #+BEGIN_SRC scheme (syntax (some (random () "yes" 'here))) #+END_SRC #+RESULTS: : '(# (# () # (# #))) Most syntax-case procedures return syntax objects. This is generally the best way to generate the macro. Usually your syntax case will return via (syntax (+ exp 1)). For example: #+BEGIN_SRC scheme (define-syntax double (lambda (x) (syntax-case x () ((_ exp) (syntax (* exp exp)))))) #+END_SRC Notice that within the syntax procedure "exp" was recognized as a pattern variable. A pattern variable may only be referenced with a syntax form. Also, since many macros use the syntax procedure, scheme has a shortcut for it: "#'". For example ~#'foo~ turns into ~(syntax foo)~. So syntax-case has a "guard" value. What is that for? The guard is a way of testing if the pattern has certain properties. If it does, then syntax-case will produce the new syntax. For example: #+BEGIN_SRC scheme (define-syntax sum-all-but-the-first (lambda (x) (syntax-case x () ((sum-all-but-the-first exp exp* ...) (and (number? (syntax->datum (syntax exp))) (number? (syntax->datum (syntax exp*))) (number? (syntax->datum (syntax ...)))) (syntax (+ exp* ...)))))) #+END_SRC ** what is a continuation? :answer: A continuation is the code that is run after a given function or expression is returned. :END: ** consider the following snippet of code #+BEGIN_SRC scheme (define (silly) (bar) (+ 5 2) (- 4 1)) #+END_SRC what is the continuation after (bar) is run? :answer: (+ 5 2) (- 4 1) :END: ** what does latent typing mean? :answer: Latent typing means that there is no way to determine that a variable in scheme will ALWAYS be of a certain type. It's type may change during execution. :END: * guild lint file.scm to lint guile source code! * decoding values from POST requests https://stackoverflow.com/questions/46656074/how-to-read-post-data-in-the-guile-web-server https://git.sr.ht/~amz3/guile-gotofish/tree/master/web/decode.scm https://git.sr.ht/~amz3/guile-gotofish/tree This is the guy who made the above code: amirouche@hyper.dev it is currently licensed under the GPL, but guile is LGPL... * SICP ** 1.2.2 Tree Recursion There are two kinds of processes in scheme: recursive and iterative. An iterative process requires less memory. Since it need less memory, it is usually faster. The recursive process, requires more memory with each call to the procedure. Let's take an example from the fibonacci sequence. Here is the fibonacci sequence defined as a recursive process. #+BEGIN_SRC scheme (define (fib n) (cond ((= n 1) 1) ((= n 2) 1) (else (+ (fib (- n 1)) (fib (- n 2)))))) (fib 35) #+END_SRC #+RESULTS: : 9227465 ..<............ fib5 <.......... ... ___________/ \___________ . ... / . ..... \ . .. fib4 . . . . . fib3 . .. ____/. \____ .. . __/ \__ . .. / . . .. \ . .. / . . \ . .. fib3 . . fib2 . . fib2 . . fib1 . .. / . \ . . / \ . . / \ ... . | . .. / . . \ . . / . \ . . / . \ . . 1 . . fib2 . . fib1. .fib1 . fib0 . .fib1. . fib0 . . . . / \ . . | . . | . . | . . | . . | . .> V / . \ . 1 . . 1 . . 0 . . 1 . . 0 .. . fib1 .. fib0.. . . . . . V . .. . . | . . | . .> .>. . . ..>. .> . 1 . . 0 . . . . . .>. .. Contrast this with the fibonacci sequence defined as an iterative process. #+BEGIN_SRC scheme (define (fib n) (fib-iter 1 0 n)) (define (fib-iter a b count) (if (= count 0) b (fib-iter (+ a b) a (- count 1)))) (fib 35) #+END_SRC #+RESULTS: : 9227465 *** DONE *Exercise 1.11:* A function f is defined by the rule that CLOSED: [2020-06-04 Thu 19:50] :LOGBOOK: - State "DONE" from [2020-06-04 Thu 19:50] :END: {f(n) = n} if {n < 3} and {f(n)} = {f(n-1)} + {2f(n-2)} + {3f(n-3)} if {n \ge 3}. Write a procedure that computes f by means of a recursive process. #+BEGIN_SRC scheme (define (random-func n) (if (< n 3) n (+ (random-func (- n 1)) (* 2 (random-func (- n 2))) (* 3 (random-func (- n 3)))))) #+END_SRC Write a procedure that computes f by means of an iterative process. n < 3 ? n n >= 3 ? {f(n-1)} + {2f(n-2)} + {3f(n-3)} 0, 1, 2, 4, 11, 25 (f 3) (+ (f 2) (* 2 (f 1)) (* 3 (f 0))) (+ 2 (* 2 1) 0) (+ 2 2) 4 (f 4) (+ (f 3) (f 2) (f 1)) (+ 4 (* 2 2) (* 3 1)) (+ 4 4 3) (+ 11) (f 5) (+ (f 4) (* 2 (f 3)) (* 3 (f 2))) (+ 11 (* 2 4) 6) (+ 11 8 6) 25 (count 3 2 1 0) count = 3 n_1 = 2 n_2 = 1 n_3 = 0 sum = (+ n_1 (* 2 n_2) (* 3 n_3)) = (+ 2 (* 2 2)) (* 3 0) = 4 count = (- count 1) L (count 4 4 2 1 0) n_1 = 4 n_2 = 2 n_3 = 1 solving for the 4th number in the sequence. r(5(n_3)) r(5(n_2)) r(5(n_2)) r(5) r(4(n_3) r(4(n_2)) r(4(n_1)) r(4) r(3(n_3) r(3(n_2)) r(3(n_1)) r(3) 0 1 2 3 4 5 0, 1, 2, 4, 11, 25 Solution: #+BEGIN_SRC scheme (define (random-func n) (if (and (< n 3) (> n -1)) n (let loop ((count n) ;; n_1 is fib (count - 1) (n_1 2) ;; n_2 is fib (count - 2) (n_2 1) (n_3 0)) (cond ((= count 3) (+ n_1 (* 2 n_2) (* 3 n_3))) (else (loop (1- count) (+ n_1 (* 2 n_2) (* 3 n_3)) n_1 n_2)))))) #+END_SRC This does not work. #+BEGIN_SRC scheme (define (random-func n) (if (and (< n 3) (> n -1)) n (let loop ((count n) ;; n_1 is random-func (count - 1) (n_1 2) ;; n_2 is random-func (count - 2) (n_2 1) (n_3 0)) (cond ((= count 3) 4) (else (loop (1- count) (+ n_1 (* 2 n_2) (* 3 n_3)) n_1 n_2)))))) #+END_SRC Works! #+BEGIN_SRC scheme (define (random-func n) (let loop ((count n) ;; n_1 is random-func (count - 1) (n_1 2) ;; n_2 is random-func (count - 2) (n_2 1) (n_3 0)) (if (and (< count 3) (> count -1)) n_1 (loop (1- count) (+ n_1 (* 2 n_2) (* 3 n_3)) n_1 n_2)))) #+END_SRC *** DONE *Exercise 1.12:* The following pattern of numbers is called CLOSED: [2020-06-05 Fri 16:42] :LOGBOOK: - State "DONE" from [2020-06-05 Fri 16:42] :END: “Pascal’s triangle”. 1 1 1 1 2 1 1 3 3 1 1 4 6 4 1 . . . The numbers at the edge of the triangle are all 1, and each number inside the triangle is the sum of the two numbers above it.(4) Write a procedure that computes elements of Pascal’s triangle by means of a recursive process. Well in order to solve this problem, I'll need to find a way to refer to specific numbers of the triangle. I will consider the topmost one to be at the origin in the Cartesian X-Y plane. |---+---------+---------+---------+---------+--------+--------+--------+---| | | | | | *1* | | | | | | | | | | (0,0) | | | | | |---+---------+---------+---------+---------+--------+--------+--------+---| | | | | 1 | | 1 | | | | | | | | (-1,-1) | (0, -1) | (1,-1) | | | | |---+---------+---------+---------+---------+--------+--------+--------+---| | | | 1 | | 2 | | 1 | | | | | | (-2,-2) | (-1,-2) | (0,-2) | (1,-2) | (2,-2) | | | |---+---------+---------+---------+---------+--------+--------+--------+---| | | 1 | | 3 | | 3 | | 1 | | | | (-3,-3) | (-2,-3) | (-1,-3) | (0,-3) | (1,-3) | (2,-3) | (3,-3) | | |---+---------+---------+---------+---------+--------+--------+--------+---| There are some points in pascal's triangle that are undefined: (0, -1), (-1,-2), (1, -2), (-2, -3), (0, -3), (2, -3). |---+---+---+---+---+---+----+----+----+----+-----+----+-----+----+----+----+----+---+---+---+---+---+---| | | | | | | | | | | | | 1 | | | | | | | | | | | | | | | | | | | | | | | 1 | | 1 | | | | | | | | | | | | | | | | | | | | | 1 | | 2 | | 1 | | | | | | | | | | | | | | | | | | | 1 | | 3 | | 3 | | 1 | | | | | | | | | | | | | | | | | 1 | | 4 | | 6 | | 4 | | 1 | | | | | | | | | | | | | | | 1 | | 5 | | 10 | | 10 | | 5 | | 1 | | | | | | | | | | | | | 1 | | 6 | | 15 | | 20 | | 15 | | 6 | | 1 | | | | | | | | | | | 1 | | 7 | | 21 | | 35 | | 35 | | 21 | | 7 | | 1 | | | | | | | | | 1 | | 8 | | 28 | | 56 | | 70 | | 56 | | 28 | | 8 | | 1 | | | | | | | 1 | | 9 | | 36 | | 84 | | 126 | | 126 | | 84 | | 36 | | 9 | | 1 | | | Well I have already discovered some interesting things: - If we divide pascal's triangle by a vertical line through its center, then the left side and the right side of pascal's triangle would have the same values. Said more formally: consider two points: (x_1, y_1) and (x_2, y_2). If y_1 = y_2, then the value at (x_1, y_1) = (x_1, y_1). - Suppose that I define a recursive procedure called pascal and (pascal 0 0) and (pascal -50, 5) refer to the values at (0, 0) and (-50 5) respectfully. *THEN* (pascal -10 -4) = (pascal 10 -4). - (pascal x_1 y_1) is undefined if x is odd and y is even or when x is even and y is odd. When y is odd, then when even x values are undefined. When y is even, then odd values of x are undefined. This is a kind of off the cuff, but pascal could be defined something like: #+BEGIN_SRC scheme (define (pascal x y) (if (or (and (odd? x) (even? y)) (and (even? x) (odd? y)) (< y 1)) (display "x cannot be even while y is odd and vice versa.\n") (let ([x (abs x)]) (if (= x (abs y)) 1 (+ (pascal (- x 1) (+ 1 y)) (pascal (+ x 1) (+ 1 y))))))) #+END_SRC *** TODO *Exercise 1.13:* Prove that tho following: {\text{Fib}(n)} is the closest integer to {\varphi^n / \sqrt{5}}, where \varphi = {(1 + \sqrt{5}) / 2}. Hint: Let \psi = {(1 - \sqrt{5}) / 2}. Use induction and the definition of the Fibonacci numbers (see *note 1.2.2::) to prove that {\text{Fib}(n)} = {(\varphi^n - \psi^n) / \sqrt{5}}. fibonacci(n) is the closest integer to (/ (exp ω n) (sqrt 5)), where ω = (/ 2 (+ 1 (sqrt 5))) 4 + 5 ÷ 8 * 3^9 PEMDAS * The Little Schemer ** Rule 1 The procedure car only /works/ on lists with things in them. Trying to use car on anything else, will either return an error, or return #null. Car returns the first item in the list. *** Here are some lists. This creates a list. #+BEGIN_SRC scheme (list "a" "b" "c") #+END_SRC #+RESULTS: | a | b | c | This is a list. #+BEGIN_SRC scheme (quote ("a" "b")) #+END_SRC #+RESULTS: | a | b | This is just shorthand for =(quote ("a" "b"))= #+BEGIN_SRC scheme '("a" "b") #+END_SRC #+RESULTS: | a | b | This creates a list. #+BEGIN_SRC scheme (cons "a" (cons "b" '())) #+END_SRC This also creates a list. #+BEGIN_SRC scheme (cons (quote +) (cons 1 (cons 2 '()))) #+END_SRC #+RESULTS: | + | 1 | 2 | This is a list. #+BEGIN_SRC scheme '(a b) #+END_SRC #+BEGIN_SRC scheme (car (list "a" "b")) #+END_SRC #+RESULTS: : a This is also a list. #+BEGIN_SRC scheme (list 'a 'b (list 'd 'c 'e) (list 1 4 9 2) (list "the" " apple") 3 9 (quote ("a" "z"))) #+END_SRC *** Let's use car! Now let's use the procedure car, which returns the first item in the list. #+BEGIN_SRC scheme (car (list 'a 'b 'c)) #+END_SRC #+RESULTS: : a #+BEGIN_SRC scheme (car (quote ("a" "b"))) #+END_SRC =car= can also return a list! #+BEGIN_SRC scheme (car '((list 1 9 3) "2" 3)) #+END_SRC But car returns null on the empty list. The empty list is ='()=. #+BEGIN_SRC scheme (car '()) #+END_SRC This results in an error. Car doesn't work on non lists. #+BEGIN_SRC scheme (car 4) #+END_SRC #+RESULTS: This results in #null. There is no first item in the empty list. #+BEGIN_SRC scheme (car '()) #+END_SRC #+RESULTS: This results in an error. A procedure is not a list. #+BEGIN_SRC scheme (car (define (procedure) (display "hello World"))) #+END_SRC #+RESULTS: This results in an error. An s expression is not a list. #+BEGIN_SRC scheme (car (let loop ([n 5]) (+ n (loop (- n 1))))) #+END_SRC * learning more guile great information is here: https://notabug.org/ZelphirKaltstahl/awesome-guile/src/master/list.md * thinks I can do