~>
~>>
8.18
top
← prev up next →

#lang axeπŸ”— i

jinzhouz

Some handy tools that you might need. Just like the axe in your hand. Source

#lang axe package: axe

  • #lang axe export all identifiers from #lang racket. Thus you can safely replace #lang racket with #lang axe.

  • #lang axe re-exports lexi-lambda’s Generic Collections which provides a generic interface for all racket collections.

1Reader ExtensionπŸ”— i

Reader extension is enabled by the #lang axe line on the top of your source file.

1.1Raw StringπŸ”— i

r"raw string"
r’raw string’

This syntax is borrowed from Python. If the string literals you are trying to write contains lots of (mixed) quotes, or escaped forms, raw string can help a lot.

For example if we want to match a single digit we have to compose regular expression: (pregexp"\\d"). Note that we have to write \\ to make racket’s reader happy and make us unhappy.

With this reader extension, the \\ character is interpreted as raw. But note that \" is interpreted as " in r"\"" form. The same goes to ' in r'\''.

>

(listr'\t\a\b\c\d1円\'quote\'')

'("\\t\\a\\b\\c\\d\1円 'quote'")

If your your string contains lots of mixed quotes, the three quotes version might be more convenient:
r"""raw string"""
r”’raw string”’

Here’s an example:

>

(listr'''mixe'singlequote'with"doublequote""''')

'("mixe 'single quote' with \"double quote\"\"")

Note that """ or '''' are matched eagerly. So that in r"""ends with"""", racket will treat the last quote as the begin of another quoted string.

Now we have an easy way to read in raw strings, we still got some troubles building up regular expressions. Thus we provide pregexp-raw and regexp-raw to build up regular expressions from raw strings.

>

(regexpr"(\t*)1円")

#rx"(\\t*)\1円"

>

(pregexp-rawr"(\t*)1円")

#px"(\t*)\1円"

syntax

( regex-escape-rawraw-string)

Parse the escaped characters in raw strings into. For example If you write r"\t", it is equivalent to string "\\t". regex-escape-raw will parse it into "\t". While raw strings like "\1円" will be used by regexp, so it will remains the same.

Example:
> (regex-escape-raw"\\a\\b\\t\\n\1円")

"\a\b\t\n\1円"

syntax

( pregexp-rawraw-string)

syntax

( regexp-rawraw-string)

It is a wrapper of regex-escape-raw, so that you can create regular expressions directly from raw strings.
(regexp(regex-escape-rawraw-string))
(pregexp(regex-escape-rawraw-string))

1.2Raw regexpπŸ”— i

#rx/raw regexp/
#px/raw regexp/
r/raw regexp string/

Typing regular expressions could be difficult in racket. In python, we can write a regular expression in raw mode: r"(\t*)1円". When translated into racket, we have #px"(\t*)\1円". Note that we need to add another \ character to escape \1円.

It is inconvenient. Luckily we have #lang at-exp so that we can do things like this:

#lang at-exp racket/base

>

(regexp-match@regexp{(.*)1円}"123123")

'("1231" "123")

1円 is one example that you do NOT want something to be escaped by racket reader. But sometimes you do need it: such as when type @px"\t", you actually wants to match the #\tab. If we use at-exp for it, it will treat \t as two characters: \\ and t. Which will not be recognized as #\tab character in racket’s "pregexp" compiler, nor "regexp" of course.

Thus we introduce the new form:

(regexp-match#px/(\t*)1円/"\t\t")

reports '("\t\t""\t"). That means the you can write raw regexp and enclose it with #px/ and /. The same goes to #rx/raw/

The "raw regexp string" is like raw regular expressions, but they can be used as replace string in regexp-replace. The following two forms are equal:

#px/(\t*)1円/
(pregexpr/(\t*)1円/)

And the r/raw regex string/ can be use where regexp strings are needed:

(regexp-replace#px/(\d+)/"abc123xyz"r/-1円-/)

We got result "abc-123-xyz".

1.3Quick KeywordπŸ”— i

This one is simple. You can replace #:key with :key.

1.4Applicable DictionaryπŸ”— i

Borrowed from #lang rackjure, we redefines #%app to make dictionaries applicable:

;When (dict? d) is #t
(dkey)=>(dict-refdkey#f)
(dkeydefault)=>(dict-refdkeydefault)

Note here that we don’t support rackjure’s set syntax: (dkeyvalue). We prefer clojure style. You can also use key as a procedure to retrieve the contents of a dict:

(keyd)=>(dict-refdkey)
(key#f)=>#f;unless (or (procedure? key) (dict? key))

The reason that #f is returned for the (key#f) is that we can use it together with the threading macro to fetch the contents of nested dicts:

(~>dict'a'b'c)

expands to:

('c('b('adict)))

And is applied as:

(dict-ref(dict-ref(dict-refdict'a)'b)'c)

1.5Dictionary initialization with {}πŸ”— i

Racket supports writting dict literals as:

#hash((k0. v0)(k1. v1))

It is straightforward but requires more typing than:

{k0v0k1v1...}

Especially when typing nested lists:

{k0v0,
k1{keyvalue,
keyvalue}}

Note the character , is optional here. In axe , is treated as whitespace if followed by other whitespaces. Thus you can use it as a delimiter whitespace or use it as unquote normally.

Borrowed form rackjure, we provide current-curly-dict parameter to specify the type of dict it expands to.

parameter

( current-curly-dict)procedure?

(current-curly-dictv)void?
v:procedure?
Defaults to hash. Can be set to hasheq or anything with the same (fkv......) signature.

Examples:

>

(parameterize([current-curly-dicthasheq])
{'k00,'k11})

'#hasheq((k0 . 0) (k1 . 1))

>

(parameterize([current-curly-dicthasheqv])
{'k00'k11})

'#hasheqv((k0 . 0) (k1 . 1))

1.6Lambda literalsπŸ”— i

Thanks to rackjure and curly-fn for the idea and implementation.

The clojure reader lets us to define function literals through:

#(+%%2)

It is equivalent to this in clojure:

(fn[%%2](+%%2))

Or in racket:

(λ(%%2)(+%%2))
(lambda(%%2)(+%%2))

In the #(...) form, arguments are denoted using identifiers prefixed with %.

  • % digit+ is a positional argument.

  • % on its own is an alias for %1.

  • %& is a rest argument.

  • %: id is a keyword argument.

Racket uses #( ) for vector literals by default. It is overwritten by axe. You can use #[ ] for that if needed. Besides, axe provides other forms for lambda literals: #fn(), #λ( ) and #lambda( ).

>

(sequence->list(map#(+1%)'(123)))

'(2 3 4)

>

(sequence->list(map#fn(+%%2)'(123)'(123)))

'(2 4 6)

>

(#lambda(applylist*%%&)1'(23))

'(1 2 3)

>

(#(* 1/2 %:m(*%:v%:v))#:m2#:v1)

1

>

(#(* 1/2 %:m(*%:v%:v)):m2:v1)

1

>

(#(begin%2)'ignored'used)

'used

Remember in the above example, we use :m to replace #:m, see Quick Keyword.

There are some pitfalls about lambda literals that normally you should not care about:

  • Nested lambda literals are not supported, the #( ... ) will be treated as vectors as it is in racket. Other forms such as #fn( ... ) will cause error.

  • It is safe to write '% and its variants, but not `%. That means quoted % is not treated as argument but not the quasiquote.

  • You should not use it to write complicated functions. It will be hard to read, use lambda instead.

2Handy MacrosπŸ”— i

2.1Threading MacrosπŸ”— i

(require axe/threading ) package: axe

Threading macros are first introduced in clojure. It helps us to change nested function calls into "pipelines". For example: we are calculating some values with a complicated nested function calls:

> (-(bytes-ref(string->bytes/utf-8(symbol->string'abc))1)2)

96

It would be hard to understand the data flow in this expression. It would be more clear if we convert it into pipelines using the threading macro:
> (~>'abc
symbol->string
string->bytes/utf-8
(bytes-ref1)
(-2))

96

Note that symbol->string and string->bytes/utf-8 are not enclosed in parenthesis.

~> macro also enables you to use placeholder(_) to specify the position of the arguments you want to place. So that you can achieve something like this:

> (~>'(123)
(mapsqrt_)
(apply+_)
(-20(*_2)))

11.707471260116055

You can also change the symbol for place holder to any identifier you like:

> (require(rename-inaxe[_%]))
> (~>'(123)
(mapsqrt%)
(apply+%)
(-20(*%2)))

11.707471260116055

Threading macro in axe will handle lambda well, so that the following example works correctly.

> (~>10(lambda(x)(-x1)))

9

> (~>10(lambda(x)(-x1))(lambda(x)(*xx)))

81

If the placeholder _ is included in the body of lambda expression, the ~> macro will instead replace the placeholder with the result of previous "pipeline" and generate a new lambda function.

(~>10(lambda(x)(-x1)))=>((lambda(x)(-x1))10)
(~>10(lambda(x)(-x_)))=>(lambda(x)(-x10))

So that you can do the following:

> ((~>10(lambda(x)(-x_)))1)

-9

Finally, we have some litte hack in threading macro, now you can NOT use [...] or {...} to represent function applications. But we already use {...} for dictionaries, so hope it won’t be too strange for you.

syntax

( ~>exprclause...)

Threads expr through clause. Insert expr as the second item in the first clause. (~>expr(functionarg)) is transformed into (~>expr(function_arg)) and that results in (functionexprarg).

If there are multiple clauses, thread the first clause as the second item in the second clause, recursively.

syntax

( ~>>exprclause...)

Like ~> but insert expr as the last item in clause. So that it equals to (~>>expr(functionarg_)).

syntax

( and~>exprclause...)

Works like ~>, but immediatly returns #f if any of the clause returns #f. Like and, this is short-circuiting, so the remaining steps will not be executed.

Examples:

Examples:
> (and~>'(245)
(mapadd1_)
(index-whereeven?)
(*2))

4

> (and~>'(246)
(mapadd1_)
(index-whereeven?)
(*2))

#f

syntax

( and~>>exprclause...)

Combines the threading behavior of ~>> and the short-circuiting behavior of and~>.

syntax

( lambda~>clause...)

syntax

( λ~>clause...)

Handy wrapper for (λ(arg)(~>argclause...)).

Example:
> (map(λ~>add1(*2))(range5))

#<stream>

syntax

( lambda~>>clause...)

syntax

( λ~>>clause...)

Like lambda~> but for ~>>.

syntax

( lambda~>*clause...)

syntax

( λ~>*clause...)

Equivalent to (λargs(~>argsclause...)).

syntax

( lambda~>>*clause...)

syntax

( λ~>>*clause...)

Like lambda~>* but for ~>>.

syntax

( lambda-and~>clause...)

syntax

( λ-and~>clause...)

syntax

( lambda-and~>>clause...)

syntax

( λ-and~>>clause...)

syntax

( lambda-and~>*clause...)

syntax

( λ-and~>*clause...)

syntax

( lambda-and~>>*clause...)

syntax

( λ-and~>>*clause...)

Like lambda~>, but for and~>.

The threading module, and rackjure had already provide such functionality and good documents. But rackjure do not support placeholder and threading module do not support placeholder in nested function calls like (~>expr(fun1(func2_arg))).

2.2ConditionalsπŸ”— i

This module provides alternatives to built in if, when and cond.

syntax

( if-let[binding-exprtest-expr]then-exprelse-expr)

combines if and match-let:
(let([valtest-expr])
(ifval
(match-let([binding-exprtest-expr])
then-expr)
else-expr))

syntax

( when-let[binding-exprtest-expr]body...+)

combines when and match-let:
(let([valtest-expr])
(whenval
(match-let([binding-exprtest-expr])
body...)))

syntax

( if-nottest-exprthen-exprelse-expr)

shotcut for:
(if(nottest-expr)
then-expr
else-expr)

syntax

( when-nottest-exprbody...+)

Just another implementation of unless. To make it consistent in the wording.
(when(nottest-expr)
body...+)

syntax

( cond-letbinding-exprcond-clause...)

cond-clause = [test-exprthen-body...+]
| [elsethen-body...+]
| [test-expr=>proc-expr]
| [test-expr]
As you can see in the signature, it is almost identical to the built in cond, except it allows an binding-expr for the test-expr, so that in the then-body for proc-expr, you can refer to the result of test-expr with it.

Example:
> (let([lst'(xyzabc)])
(cond-letit
[(member'alst)(lengthit)]
[else0]))

3

match-let pattern can be applied to descruct the values:

Example:
> (let([dct#hasheq((a. #f)(b. (12))(c. (1020)))])
(cond-let(listab)
[(dict-refdct'a)(+ab)]
[(dict-refdct'b)(-ab)]
[(dict-refdct'c)(*ab)]
[else0]))

-1

3Utility FunctionsπŸ”— i

3.1DictionaryπŸ”— i

(require axe/dict ) package: axe

Now axe depends on data/collection, you are safely to use it instead.

axe provide some wrappers over the dictionary related functions provided by racket/dict.

procedure

( dict-extenddictkeyvalue...)(and/cdict?immutable?)

dict:(and/cdict?immutable?)
key:any/c
value:any/c
Iterate over all the key-value pairs and update the dictionary accordingly.

Examples:
> (dict-extend'((a. 1))'b2'a3)

'((a . 3) (b . 2))

> (dict-extend#hash()'a1'b2'a3)

'#hash((a . 3) (b . 2))

procedure

( dict-extend!dictkeyvalue...)

(and/cdict?(not/cimmutable?))
dict:(and/cdict?(not/cimmutable?))
key:any/c
value:any/c
The mutable version of dict-extend.

Example:
> (dict-extend!(make-hash)'b2'a3)

procedure

( dict-merged0d1...)(and/cdict?immutable?)

d0:(and/cdict?immutable?)
d1:dict?

procedure

( dict-merge!d0d1...)(and/cdict?(not/cimmutable?))

d0:(and/cdict?(not/cimmutable?))
d1:dict?
Merge several dicts into d0. dict-merge! is the mutable version.

Examples:
> (dict-merge'((a. 1))'((b. 2)(c. 3))'((a. 4)(c. 5)))

'((a . 4) (b . 2) (c . 5))

> (defined(make-hash'((a. 1))))
> (dict-merge!d'((b. 2)(c. 3))#hash((a. 4)(c. 5)))
> d

'#hash((a . 4) (b . 2) (c . 5))

procedure

( dict-merge-withfd0d1...)(and/cdict?immutable?)

f:(any/cany/c. -> .any/c)
d0:(and/cdict?immutable?)
d1:dict?

procedure

( dict-merge-with!fd0d1...)(and/cdict?(not/cimmutable?))

f:(any/cany/c. -> .any/c)
d0:(and/cdict?(not/cimmutable?))
d1:dict?
Like dict-merge and dict-merge!. But when both dicts contain the same key, merge function f is called with arguments the value in the old dict and value in the new dict.

Examples:
> (dict-merge-withlist#hash((a. 1))'((b. 2)(c. 3))'((a. 4)(c. 5)))

'#hash((a . (1 4)) (b . 2) (c . (3 5)))

> (dict-merge-with-'((a. 5))'((b. 2)(c. 3))'((a. 4)(c. 5)))

'((a . 1) (b . 2) (c . -2))

top
← prev up next →

AltStyle γ«γ‚ˆγ£γ¦ε€‰ζ›γ•γ‚ŒγŸγƒšγƒΌγ‚Έ (->γ‚ͺγƒͺγ‚ΈγƒŠγƒ«) /