gadgets

2024-10-12

Ben McGunigle's utility collection

Upstream URL

github.com/BnMcGn/gadgets

Author

Ben McGunigle <bnmcgn@gmail.com>, Your Name <your.name@example.com>

License

Apache License, version 2.0, Specify license here
README

Gadgets

CircleCI

Ben McGunigle's collection of gadgets, odds, ends and utilities general.

symbolize

Function Parameters: entity &key package Turns a string into an upcased symbol. The symbol will be interned in the current package if a package is not specified

mkstr

Function: &rest args

Paul Graham's mkstr. Returns a string representation of the parameters.

ret

Macro: var val &body body

A single variable let that returns the variable when the body completes.

def-as-func

Macro: var func-form

Set a variable in the function namespace.

fetch-keyword

Function: key alist &key in-list

Find if a key is in a list, return the next item after it. if in-list is true, test the first element of any sublists for the key and if found return the rest of the sublist. A bit coarser in function than getf. Will tolerate improper plists.

extract-keywords

Function: keywords alist &key in-list test

Traverses a plist or lambda list, removing the specified keywords and the value that immediately follows each. Found key/value pairs are returned as a plist. The cleaned list is returned as the second value.

This, or the related macro bind-extracted-keywords, is particularly useful for adding features to macros. It will strip out added keywords from parameter lists, allowing the remainder to be passed to the original macro processing code.

bind-extracted-keywords

Macro: (source remainder &rest keys) body

Removes the keywords named in keys, with their accompanying parameters, from the expression supplied in source. Source, minus the keys, is bound to remainder. The names of the keys are used for bindings for the accompanying values.

(bind-extracted-keywords ((1 2 :x 3) data :x) <body>)

Results in the body being executed with data bound to (1 2) and x bound to 3.

quotef

Macro: setf-spec

Cause the item at the targeted location to become quoted:

> (defparameter *list* '(zero one two three))
> (quotef (elt *list* 2))
> *list*
(ZERO ONE 'TWO THREE)

quoted-p

Function: item

Macro utility to test if an item has been quoted by the macro user.

quoted-symbol-p

Function: item

Macro utility to test if an item has been passed into a macro as a quoted symbol. If so, returns the symbol without the quote.

quoted-list-p

Function: item

Utility to test if a macro parameter is a quoted list. Returns the list if it is.

use-package-with-shadowing

Function: package &optional target-package

Like use-package, but shadows existing symbols from target-package without asking. Target-package is the current package unless otherwise specified.

String

capitalize-first

Function: item

Returns a string representation of item with the first letter capitalized and the remaining characters lower-case, where applicable. Item can be a string or a symbol.

to-lowercase

Function: item

Returns a lowercase copy of the supplied string.

to-uppercase

Function: item

Returns an uppercase copy of the supplied string.

string-unless-number

Function: item

For processing user input. Return the input as a string unless it can be recognized as an integer.

symbol-unless-number

Function: item

Convert the input string into a symbol unless it can be converted into a number.

boolify

Function: item

Attempts to guess when a string or number should be interpreted as T. Postive integers and strings like "true" and "yes" will be interpreted as true. Non-empty lists and sequences are true. Most other things are NIL. This function is useful for interactions in which a human is expected to answer a true or false question.

not-empty

Function: sequence

A predicate to detect 0 length sequences.

sequences-start-same

Function: seq seq2

Given two sequences, are they the same until one runs out? This function does not care which sequence contains the other. Use sequence-starts-with for more specific results.

sequence-starts-with

Function: seq testseq

Does the sequence begin with the test sequence?

sequence-ends-with

Function: seq testseq

Does the sequence end with the test sequence?

string-join

Function: filler strings

Returns a string consisting of one each of the items in strings with filler interspersed between all of the items.

> (string-join " and " '("one" "two" "three"))
"one and two and three"

whitespace-characters

List

(#\Space #\Newline #\Backspace #\Tab #\Linefeed #\Page #\Return #\Rubout)

string-equal*

Function: a b

Broad version of string-equal. Will take input that is not a string or symbol.

string-equal-case

Function:

A case sensitive version of string-equal.

truncate-string

Function: str &key length indicator

Trim a string to the length specified by length - default 20, appending instead the string specified by indicator - default "...".

Keyed collection

assoc-cdr

Function: &rest keys-and-alists

A shortcut for (cdr (assoc ...)).

assoc-all

Function: item alist &key test

Gets all items associated with a key, not just the first. Returns a list

(assoc-all :a '((:a . 1) (:b . 2) (:c . 3) (:a . 4)))
=> (1 4)

assoc-or

Function: keys alist

Finds the first key in keys that has a match in alist. Uses equal to match strings.

alist-p

Function: item

Does an item appear to be an assoc-list?

plist-p

Function: item

Determine if an item qualifies as a plist

invert-hash-table

Function: hash &key test mode

Returns a new hash table with keys and values swapped:

(:a 1 :b 3 :c 5) => (1 :a 3 :b 5 :c)

The hash table test can be set with :test. The method of value collection can be controlled with :mode. Modes are those available for cl-hash-util:collecting-hash-table.

rekey

Function: store mapping &key ignore-missing test

Takes a store (one of hash, alist or plist) and a mapping (also a hash, alist or plist) and returns a new hash table with the values from store rekeyed according to the oldkey -> newkey pairs found in mapping. Ignore-missing instructs rekey on what to do when a key is found in store but not in mapping. If true, rekey will drop the item that has no match. When false, it will include the item as is.

The result hash table will be created with the supplied test parameter.

do-alist

Macro: (key value source) &body body

Iterate over an alist.

do-hash-table

Macro: (key value source) &body body

Iterate over a hash table

key-in-hash?

Function: key hash

Check if a hash table has a key.

Shortcut for (nth-value 1 (gethash key hash))

hash-table-source

Function: hash

Returns a source code representation of a hash table.

List and sequence

xsubseq

Function: sequence start end &key type

Returns copy of sequence with start->end chopped out of it.

sequence->list

Function: seq

Returns a list consisting of the contents of the input sequence.

last-car

Function: list

Returns the last item in a list proper.

chunk

Function: n list

Breaks a list up into n sized chunks.

>(chunk 3 '(a b c d e f g))
((A B C) (D E F) (G))

flatten-1

Function: list

Flattens conses found in the top level of a list. Nils in the top level will be removed.

(flatten-1 '((1 2 3) nil (nil) ((4 5) (6 7))))
(1 2 3 NIL (4 5) (6 7)) 

flatten-when

Function: predicate items &key descend-all

Recursively flattens any conses found in items if the predicate returns true on them. Will not flatten NILs unless the predicate indicates it. The predicate will not be called on non-cons items. Flatten-when will not normally descend into lists which it will not flatten, passing unchanged any list or cons item that fails the predicate. To cause it to descend into non-matching portions of the tree, set the :descend-all keyword.

flatten-1-when

Function: predicate items

Returns a list with any conses in it flattened if predicate returns true when called with that item. Will not flatten NILs unless the predicate indicates it. The predicate will not be called on non-cons items.

part-on-index

Function: list/seq index &key fail

Divides a list into two parts at the specified index. The two parts are returned as values. If the index is too large for the sequence, part-on-index will silently return the sequence as the first value. Set the :fail keyword T to raise an error instead.

part-on-true

Function: predicate list/seq &key fail

Divides a list or sequence into two parts, with the second part starting with the first item to cause test to return true. The two parts of the sequence are returned as values. If a dividing point is not found, part-on-true will return the whole sequence as the first value. If you wish it to raise an error instead, set the :fail parameter to true.

part-after-true

Function: predicate list/seq &key fail

Like part-on-true, but includes the first matching item in the first list.

remove-if-member

Function: sequence things &key key test

Returns a copy of sequence without items that are a member of things. You can modify the test function which is used by member - default eq - with the :test keyword. The :key keyword allows a single parameter function that receives a sequence item and should return a modified item for comparison to the items in things.

splitfilter

Function: predicate list

Performs the remove-if-not and remove-if operations on a list simultaneously, returning each list as the first and second values respectively.

split-sequence-on-subseq

Function: subseq/s sequence

Searches for a phrase or phrases in a sequence. When it finds one, it returns as values the subsequence up to the match, the subsequence after the match, and the matching subsequence. If no match is found, the whole sequence is returned as the first value.

first-match

Function: predicate list

Like the built in function some, first-match returns the first item in the list that evaluates true in the predicate. First-match uses the second value to indicate success, unlike some.

first-match-index

Function: predicate list

Returns the index of the first item in list that satisfies predicate.

ordered-unique

Function: list &key test

Returns a unique list of the items in list in the order in which they first appear.

do-list-with-rest

Macro: (head tail source) &body body

A dolist for situations where access to the whole list is needed. Do-list-with-rest will iterate through the list supplied in source, initially binding the list to tail. On each iteration an item is removed from the first position of tail and pushed onto head. Note that the contents of head will be in reverse from those in source

list-set-place

Function: list index value padding

Returns a copy of the list with index set to value. If index is beyond the length of the list, pad out the list with the value in padding.

Numerical

range

Function: start &optional stop step

Creates a list containing a sequential range of integers. By default the range runs from 0 to one below the supplied stop value:

(range 3) -> (0 1 2)

If a second parameter is supplied, the first is treated as a starting value, and the second as a stop:

(range 7 10) -> (7 8 9)

The third parameter specifies a step size:

(range 0 10 2) -> (0 2 4 6 8)

A negative step parameter causes the range to travel down from the start to the stop:

(range 10 5) -> (10 9 8 7 6)

relative-to-range

Function: start end num

Returns a value indicating where num is positioned relative to start and end. If num lies between start and end, the return value will be between 0.0 and 1.0.

as-in-range

Function: start end num

Complement of relative-of-range function. Treats num as if it were a fraction of the range specified by start and end. Returns the absolute number that results.

Execution control

or2

Macro: &rest clauses

A version of or that bases its decision on the second value of each clause. Forms that return no second value are considered T.

do-window

Macro: (var/s source &key size step start-padding) &body body

Like dolist, iterates over a list, but instead of binding a single list item per iteration binds a segment of the list as a sliding window.

(do-window (x '(1 2 3 4 5 6)) ...)

will execute the body 5 times with x bound respectively to:

(1 2) (2 3) (3 4) (4 5) (5 6)

The step keyword adjusts how far the window slides per iteration. A destructuring spec can be provided in place of the variable. Therefore do-window can be used to iterate over a plist like so:

(do-window ((k v) '(:a 1 :b 2 :c 3) :step 2) ...)

Each key and value will be bound to k and v, respectively.

The size keyword allows adjustment of the window size.

Leading padding may be provided to do-window with the start-padding keyword.

tryit

Macro: &body body

Execute the code in the body, returning T as the second value if the code executes without error, but returning (NIL NIL) if an exception is thrown. This provides a quick way to turn an error into a boolean value.

WARNING: This isn't always a great idea for production code. Tryit will mask all raised errors, So if your code throws an error outside of what you expected, you won't be warned of the variance.

three-way

Macro: test minus-clause zero-clause plus-clause

If test results in a number that is less than zero, minus-clause will be evaluated. If zero, then zero-clause. Otherwise, plus-clause.

preserve-other-values

Macro: expression func

Take the values returned by expression, pass the first of them to func, returning its first value as the primary value and appending the remaining values from expression as unchanged.

(1+ (values 1 2 3)) => 2
(preserve-other-values (values 1 2 3)
                     #'1+) => 2 2 3

maplist/step

Function: func step list &rest more-lists

Maps through the supplied lists, passing a step-sized chunk from each into func. Returns a list of the results. The lists should all be the same length. That length should be divisible by the value specified in step.

map-by-2

Function: func list

Maps over a list two items at a time. The items are passed to the function as first and second parameters.

mapcan-by-2

Function: func list

This function is ideal for processing plists. As with map-by-2, func will receive items 2 at a time as the first and second parameters. It should return a list. For example:

(mapcan-by-2 (lambda (key value) (list key value)) <some-list>)

will copy a plist with no changes.

map-assoc

Function: func alist

Map over an assoc list, with the key and value from each pair being sent to func as, respectively, the first and second parameters.

map-improper

Function: func list?

Map over a list, proper or not. The return mapping will be a proper list.

mapc-improper

Function: func list?

Mapc over a list, proper or not. Original list is returned. Like mapc, mapc-improper is used for side effects only.

return-on-true

Macro: clause &optional from-target

Executes return/return-from on the result of clause if it is true

try-awhile

Function: function &key sleep wait on-success on-fail

Will continue to call function until either it returns success or a given amount of time elapses. Duration can be set with the :wait keyword. It defaults to 1 second. Try-awhile will sleep between function calls unless the :sleep keyword is set to nil. Default sleep is 0.001 of a second.

Try-awhile will return the function value on success or nil on failure. If a function is supplied to the :on-success argument, it will be executed if the function succeeds and its result will be returned instead. The :on-fail keyword may be used to supply a function that will be run if the time elapses without a successful function run. Its result will be returned instead of the default nil.

Try-awhile blocks until completion.

Tree

part-tree

Function: test tree

Divides the s-expression supplied in tree into an inner and an outer portion. The outer portion is returned in the first value as a closure. The inner portion is returned as the second value. The inner portion consists of the first part of the tree that passes test. The tree is traversed breadth-first.

> (part-tree
    (lambda (x) (eq 'deepest (car (ensure-list x))))
    '(deep (deeper (deeperer (deepest (deepester you-are-here))))))
#<CLOSURE (LAMBDA (GADGETS::X) :IN GADGETS:PART-TREE) {C19C81D}>
(DEEPEST (DEEPESTER YOU-ARE-HERE))

> (funcall * :xyz)
(DEEP (DEEPER (DEEPERER :XYZ)))

The returned closure should be called with a single argument. It will return the outer portion with the supplied argument in place of the inner portion.

File and OS

do-file-by-line

Macro: (line stream-or-path) &body body

Steps through a file or stream, one line at a time, binding the contents of the line to the variable specified in line. If a pathname is supplied, the file will be closed on completion or other exit. Streams will not be closed.

map-file-by-line

Function: function stream-or-path

A wrapper around do-file-by-line. The specified file or stream will be passed to the supplied function one line at a time. The function results will be accumulated in a list and returned.

with-file-lock

Macro: (path &key interval) &body body

Get an exclusive lock on a file. If lock cannot be obtained, keep trying after waiting a while

encode-time-delta

Function: second minute hour day

A utility function for creating a time delta. Returns the time delta as an integer representing seconds.

make-clock

Function: ticks-per-second

Returns a timer closure that, on execution, returns the number of units of time elapsed since make-clock returned it.

homedir-relative-pathname

Function: name

Merge the supplied pathname onto the path of the current user's home directory.

call-with-temporary-directory

Function: thunk &key want-pathname-p

Generates a temporary directory in the appropriate location (specified by uiop), calls the supplied function, then cleans up the directory.

By default, the created pathname is passed to the function. If want-pathname-p is set to NIL, the current directory will instead be set to the temporary directory for the duration of the call.

with-temporary-directory

Macro: (&key pathname) &body body

Generates a temporary directory in the appropriate location (specified by uiop), executes the body of the macro, then cleans up the directory.

When a symbol is supplied to :pathname the temporary name will be bound to it. Otherwise the current directory will be set to the temporary directory for the duration of body execution.

Debugging printers

Certain forms are awkward to debug using plain print. These tools provide some extra options.

pif

Macro: test then &optional else

This macro is a drop in replacement for if. It prints the test expression and the result of the test, indicating which way the if statement has branched.

print-lambda

Macro: (&rest args) &body body

A drop in replacement for lambda that behaves as trace does for defined functions.

print-cond

Macro: &rest clauses

A verbose drop in replacement for cond.

print-and

Macro: &rest forms

A verbose drop in replacement for and.

print-all-values

Macro: expr

Like print, but prints - and passes on - all values received. Useful for debugging expressions that return multiple values.

dump

Function: thing

For situations where print can't reach out. Put an item into storage for later retrieval by dive.

dive

Function

Retrieve dumped item.

Dependencies (6)

  • alexandria
  • cl-hash-util
  • cl-utilities
  • prove
  • split-sequence
  • uiop

Dependents (0)

    • GitHub
    • Quicklisp