From 3d7ca1f5ac7a0631d3808a7bbb44ee0a5f13cec7 Mon Sep 17 00:00:00 2001 From: Goderich Date: Mon, 12 Jul 2021 11:18:01 +0800 Subject: [PATCH] Add anaphoric map and filter (squashed commit) --- afilter.rkt | 13 +++++++++++++ amap.rkt | 13 +++++++++++++ scribblings/anaphoric.scrbl | 31 +++++++++++++++++++++++++++++-- test/afilter-test.rkt | 21 +++++++++++++++++++++ test/amap-test.rkt | 19 +++++++++++++++++++ 5 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 afilter.rkt create mode 100644 amap.rkt create mode 100644 test/afilter-test.rkt create mode 100644 test/amap-test.rkt diff --git a/afilter.rkt b/afilter.rkt new file mode 100644 index 0000000..5dce0da --- /dev/null +++ b/afilter.rkt @@ -0,0 +1,13 @@ +#lang racket/base + +(provide afilter it) +(require anaphoric/it + racket/stxparam + (for-syntax racket/base)) + +(define-syntax-rule (afilter body lst) + (let ([func + (λ (var) + (syntax-parameterize ([it (make-rename-transformer #'var)]) + body))]) + (filter func lst))) diff --git a/amap.rkt b/amap.rkt new file mode 100644 index 0000000..a269be4 --- /dev/null +++ b/amap.rkt @@ -0,0 +1,13 @@ +#lang racket/base + +(provide amap it) +(require anaphoric/it + racket/stxparam + (for-syntax racket/base)) + +(define-syntax-rule (amap body lst) + (let ([func + (λ (var) + (syntax-parameterize ([it (make-rename-transformer #'var)]) + body))]) + (map func lst))) diff --git a/scribblings/anaphoric.scrbl b/scribblings/anaphoric.scrbl index 1a65efc..a562b9c 100644 --- a/scribblings/anaphoric.scrbl +++ b/scribblings/anaphoric.scrbl @@ -2,7 +2,7 @@ @require[@for-label[anaphoric racket/base]] -@title{Anaphoric conditionals} +@title{Anaphoric macros} @author[@author+email["Suzanne Soy" "racket@suzanne.soy"]] @defmodule[anaphoric] @@ -10,7 +10,7 @@ @section{Overview} This package provides anaphoric versions of @racket[if], -@racket[when] and @racket[cond]. These bind the syntax +@racket[when], @racket[cond], @racket[map], and @racket[filter]. These bind the syntax parameter @racket[it] to the value produced by the condition expression. @@ -155,3 +155,30 @@ using @racket[it]. @racket[conditionᵢ] is successful. } +@section{Anaphoric map and filter} + +@defform[[(amap body lst)]]{ + Anaphoric @racket[map]. Binds the syntax parameter @racket[it] + in the @racketid[body], and maps it over the list @racketid[lst]. Effectively the same + as wrapping the @racketid[body] in a @racket[lambda] with an @racket[it] parameter. Unlike @racket[map], @racket[amap] + only works on a single list. + + @racket[amap] works with nested function calls: + + @racketblock[(amap (string-append (string-upcase it) "!") + '("apple" "banana"))] + + The syntax parameter @racket[it] may be used multiple times in the procedure: + + @racketblock[(amap (* it it) '(1 2 3))] +} + +@defform[[(afilter body lst)]]{ + Anaphoric @racket[filter]. Binds the syntax parameter @racket[it] + in the @racketid[body], and filters the list @racketid[lst] using it. Effectively the same + as wrapping the body in a @racket[lambda] with an @racket[it] parameter. + + @racket[afilter] works with nested function calls: + + @racketblock[(afilter ((* it it) . > . 50) lst)] +} diff --git a/test/afilter-test.rkt b/test/afilter-test.rkt new file mode 100644 index 0000000..190c86b --- /dev/null +++ b/test/afilter-test.rkt @@ -0,0 +1,21 @@ +#lang racket/base + +(require anaphoric/afilter + rackunit) + +(define lst '(5 6 7 8 9)) + +(check-equal? + '(6 8) + (afilter (odd? (add1 it)) lst) + "Nested function call.") + +(check-equal? + '(8 9) + (afilter ((* it it) . > . 50) lst) + "Multiple 'it' in a nested expression.") + +(check-equal? + '() + (afilter (= it 42) '()) + "Empty list.") diff --git a/test/amap-test.rkt b/test/amap-test.rkt new file mode 100644 index 0000000..3ca74f0 --- /dev/null +++ b/test/amap-test.rkt @@ -0,0 +1,19 @@ +#lang racket/base + +(require anaphoric/amap + rackunit) + +(check-equal? + '(2 3) + (amap (add1 it) '(1 2)) + "Sanity check.") + +(check-equal? + '("2" "4") + (amap (number->string (+ it it)) '(1 2)) + "Multiple 'it' in a nested expression.") + +(check-equal? + '() + (amap (/ it 0) '()) + "Empty list.")