246 lines
5.6 KiB
Markdown
246 lines
5.6 KiB
Markdown
---
|
|
title: Racket notes
|
|
date: 2020-02-29 09:00:00
|
|
tags:
|
|
- racket
|
|
categories:
|
|
- notes
|
|
keywords:
|
|
- racket
|
|
---
|
|
|
|
## Basic
|
|
|
|
```racket
|
|
#lang racket
|
|
(provide (all-defined-out))
|
|
|
|
;this is a comment
|
|
|
|
(define s "hello")
|
|
|
|
(define x 3)
|
|
(define y (+ x 2))
|
|
|
|
(define cube1
|
|
(lambda (x)
|
|
(* x (* x x))))
|
|
|
|
(define cube2
|
|
(lambda (x)
|
|
(* x x x)))
|
|
|
|
(define (cube3 x)
|
|
(* x x x))
|
|
|
|
(define (pow1 x y)
|
|
(if (=y 0)
|
|
1
|
|
(* x (pow1 x (- y 1)))))
|
|
|
|
; currying
|
|
(define pow2
|
|
(lambda (x)
|
|
(lambda (y)
|
|
(pow1 x y))))
|
|
|
|
```
|
|
|
|
### List
|
|
|
|
* Empty list: `null`
|
|
* `()` doesn"t work for `null` but `'()` does
|
|
* build a list: `(list e1 ... en)`
|
|
* Constructor: `cons`
|
|
* Access head of list: `car`
|
|
* Access tail of list: `cdr`
|
|
* Check for empty: `null?`
|
|
|
|
### Syntax
|
|
|
|
A term is either:
|
|
* An atom like `#t, #f, 34, "hi", null, 4.0, x,...`
|
|
* A special form like `define, lambda, if`
|
|
* A sequence of terms in parentheses: `(t1 t2 t3)`
|
|
* Can use `[` anything you use `(`
|
|
|
|
Remember parentheses matters! For example:
|
|
`(e)` means call e with 0 argument.
|
|
|
|
### Dynamic typing
|
|
|
|
```racket
|
|
(define lst (list #t "hi" 1 (list 2 3 4)))
|
|
```
|
|
|
|
### Cond
|
|
|
|
```racket
|
|
(define (sum3 xs)
|
|
(cond [(null? xs) 0]
|
|
[(number? (car xs)) (+ (car xs) (sum3 (cdr xs)))]
|
|
[#t 0]))
|
|
```
|
|
|
|
### What is true?
|
|
|
|
Anything that is not `#f` is true `#t`.
|
|
|
|
### Local bindings
|
|
|
|
#### let/let*/letrec
|
|
|
|
```racket
|
|
(let ([x1 e1]
|
|
[x2 e2]
|
|
...
|
|
[xn en])
|
|
e)
|
|
```
|
|
|
|
Racket uses the environment **before** the let-expression to evaluate `e1 e2 ... en`, which means if `en` uses `x1`, `x2`, that would mean some outer variables of the same name. Instead, the expressions in `let*` are evaluated in the environment produced from the previous bindings (later ones shadow) .
|
|
|
|
The expressions in `letrec` are evaluated in the environment that includes all th bindings. It is needed for mutual recursion.
|
|
|
|
### set!
|
|
|
|
Racket has assignment statements:
|
|
```racket
|
|
(set! x e)
|
|
```
|
|
|
|
Once you have side-effects, sequences are useful:
|
|
```racket
|
|
(begin e1 e2 e3)
|
|
```
|
|
|
|
### cons/mcons
|
|
|
|
`cons` produces pairs or lists. (Actually lists are just extented pairs)
|
|
```racket
|
|
(define pr (cons 1 (cons #t "hi"))) ; is a pair
|
|
(define lst (cons 1 (cons #t (cons "hi" null)))) ; is a list
|
|
```
|
|
|
|
`mcons` is another way to make pairs which allows you to change the value inside piars:
|
|
```racket
|
|
(define mpr (mcons 1 (mcons #t "hi")))
|
|
(mcar mpr) ; 1
|
|
(mcdr mpr) ; (mcons (#t "hi"))
|
|
(set-mcdr! mpr 47) ; mpr becomes (mcons 1 47)
|
|
```
|
|
|
|
Related form:
|
|
* `mcons`
|
|
* `mcar`
|
|
* `mcdr`
|
|
* `mpair?`
|
|
* `set-mcar!`
|
|
* `set-mcdr!`
|
|
|
|
## Delayed Evaluation and Thunk
|
|
|
|
In most programming languages, given `e1 e2 ... en`, the function arguments `e2, ..., en` are evaluated once before the function body is executed.
|
|
So if we define a function like:
|
|
```rkt
|
|
(define (my-if-bad x y z) (if x y z))
|
|
|
|
(define (factorial-wrong x)
|
|
(my-if-bad (= x 0)
|
|
1
|
|
(* x (factorial-wrong (- x 1)))))
|
|
```
|
|
if we use `if` instead of `my-if-bad`, `factorial-wrong` acts just like we want. But with `my-if-bad`, the function never stops because the two branches evaluate at the same time.
|
|
|
|
Thanks to lambda, we can delay the evaluation, using the fact that function bodies are not evaluated until the function gets called.
|
|
```rkt
|
|
(define (my-if x y z) (if x (y) (z)))
|
|
|
|
(define (factorial x)
|
|
(my-if (= x 0)
|
|
(lambda () 1)
|
|
(lambda () (* x (factorial (- x 1))))))
|
|
```
|
|
The general idiom of using a zero-argument function to delay evaluation is also called a **thunk** (or, thunk the argument).
|
|
|
|
By the way,
|
|
|
|
## Lazy-evaluation/Call-by-need/Promises
|
|
|
|
## Streams
|
|
|
|
A stream is an infinite sequence of values.
|
|
```racket
|
|
#lang racket
|
|
; 1 1 1 1 ...
|
|
(define ones (lambda () (cons 1 ones)))
|
|
|
|
; 1 2 3 4 ...
|
|
(define nats
|
|
(letrec ([f (lambda (x) (cons x (lambda () (f (+ x 1)))))])
|
|
(lambda () (f 1))))
|
|
|
|
; 2 4 6 8 ...
|
|
(define power-of-two
|
|
(letrec ([f (lambda (x) (cons x (lambda () (f (* x 2)))))])
|
|
(lambda () (f 2))))
|
|
|
|
; higher-order maker
|
|
(define (stream-maker fn arg)
|
|
(letrec ([f (lambda (x) (cons x (lambda () (f (fn x arg)))))])
|
|
(lambda () (f arg))))
|
|
```
|
|
|
|
## Memoization (Not Memorization)
|
|
|
|
Memoization is another idiom related to lazy evaluation that does not actually use thunks. To implement memoization we do use mutation: Whenever the function is called with an argument we have not seen before, we compute the answer and then add the result to the table.
|
|
|
|
```racket
|
|
(define fibonacci
|
|
(letrec([memo null]
|
|
[f (lambda (x)
|
|
(let ([ans (assoc x memo)])
|
|
(if ans
|
|
(cdr ans)
|
|
(let ([new-ans (if (or (= x 1) (= x 2))
|
|
1
|
|
(+ (f (- x 1))
|
|
(f (- x 2))))])
|
|
(begin
|
|
(set! memo (cons (cons x new-ans) memo))
|
|
new-ans)))))])
|
|
f))
|
|
```
|
|
|
|
## Macros
|
|
|
|
Think about these things about macros and how Racket handles them better than other macro systems(notably C/C++)
|
|
|
|
* Tokenization
|
|
* Parenthesization
|
|
* Scope
|
|
|
|
### Syntax
|
|
|
|
```racket
|
|
(define-syntax myif
|
|
(syntax-rules (then else)
|
|
[(my-if e1 then e2 else e3)
|
|
(if e1 e2 e3)]))
|
|
|
|
(define-syntax my-delay
|
|
(syntax-rules ()
|
|
[(my-delay e)
|
|
(mcons #f (lambda () e))]))
|
|
|
|
```
|
|
### Hygiene
|
|
|
|
## Recursive Datatypes Via Rackets's `struct`
|
|
|
|
[https://docs.racket-lang.org/reference/define-struct.html?q=struct#%28form._%28%28lib._racket%2Fprivate%2Fbase..rkt%29._struct%29%29](https://docs.racket-lang.org/reference/define-struct.html?q=struct#%28form._%28%28lib._racket%2Fprivate%2Fbase..rkt%29._struct%29%29)
|
|
|
|
```racket
|
|
(struct foo (bar baz quux) #:transparent
|
|
```
|