# HG changeset patch # User Steve Losh # Date 1478954826 0 # Node ID 2c76ffecc45e42dc545dd1d043d50e5b1b648774 # Parent 3d34f6a99a249573faf42248b90c9b053ac29acf Add `proportions` and `mutate-hash-values` diff -r 3d34f6a99a24 -r 2c76ffecc45e DOCUMENTATION.markdown --- a/DOCUMENTATION.markdown Thu Nov 10 14:42:47 2016 +0000 +++ b/DOCUMENTATION.markdown Sat Nov 12 12:47:06 2016 +0000 @@ -417,6 +417,20 @@ +## Package `LOSH.HASH-TABLES` + +Utilities for operating on hash tables. + +### `MUTATE-HASH-VALUES` (function) + + (MUTATE-HASH-VALUES FUNCTION HASH-TABLE) + +Replace each value in `hash-table` with the result of calling `function` on it. + + Returns the hash table. + + + ## Package `LOSH.ITERATE` Custom `iterate` drivers and clauses. @@ -887,6 +901,29 @@ +### `PROPORTIONS` (function) + + (PROPORTIONS SEQUENCE &KEY (TEST 'EQL) (FLOAT T)) + +Return a hash table containing the proportions of the items in `sequence`. + + Uses `test` for the `:test` of the hash table. + + If `float` is `t` the hash table values will be coerced to floats, otherwise + they will be left as rationals. + + Example: + + (proportions '(foo foo bar)) + => {foo 0.66666 + bar 0.33333} + + (proportions '(foo foo bar) :float nil) + => {foo 2/3 + bar 1/3} + + + ## Package `LOSH.WEIGHTLISTS` A simple data structure for choosing random items with weighted probabilities. diff -r 3d34f6a99a24 -r 2c76ffecc45e losh.lisp --- a/losh.lisp Thu Nov 10 14:42:47 2016 +0000 +++ b/losh.lisp Sat Nov 12 12:47:06 2016 +0000 @@ -1181,6 +1181,19 @@ (keywordize-clause clause)))))) +;;;; Hash Tables +(defun mutate-hash-values (function hash-table) + "Replace each value in `hash-table` with the result of calling `function` on it. + + Returns the hash table. + + " + (iterate (for (key value) :in-hashtable hash-table) + (setf (gethash key hash-table) + (funcall function value))) + hash-table) + + ;;;; Sequences (defun prefix-sums (sequence) "Return a list of the prefix sums of the numbers in `sequence`. @@ -1214,6 +1227,31 @@ (incf (gethash i result 0)) (finally (return result)))) +(defun proportions (sequence &key (test 'eql) (float t)) + "Return a hash table containing the proportions of the items in `sequence`. + + Uses `test` for the `:test` of the hash table. + + If `float` is `t` the hash table values will be coerced to floats, otherwise + they will be left as rationals. + + Example: + + (proportions '(foo foo bar)) + => {foo 0.66666 + bar 0.33333} + + (proportions '(foo foo bar) :float nil) + => {foo 2/3 + bar 1/3} + + " + (let* ((freqs (frequencies sequence :test test)) + (total (reduce #'+ (hash-table-values freqs) + :initial-value (if float 1.0 1)))) + (mutate-hash-values (lambda (v) (/ v total)) + freqs))) + (defun group-by (function sequence &key (test #'eql) (key #'identity)) "Return a hash table of the elements of `sequence` grouped by `function`. diff -r 3d34f6a99a24 -r 2c76ffecc45e make-docs.lisp --- a/make-docs.lisp Thu Nov 10 14:42:47 2016 +0000 +++ b/make-docs.lisp Sat Nov 12 12:47:06 2016 +0000 @@ -8,6 +8,7 @@ "LOSH.DEBUGGING" "LOSH.ELDRITCH-HORRORS" "LOSH.FUNCTIONS" + "LOSH.HASH-TABLES" "LOSH.ITERATE" "LOSH.LICENSING" "LOSH.LISTS" diff -r 3d34f6a99a24 -r 2c76ffecc45e package.lisp --- a/package.lisp Thu Nov 10 14:42:47 2016 +0000 +++ b/package.lisp Sat Nov 12 12:47:06 2016 +0000 @@ -141,11 +141,17 @@ #:skip-origin #:macroexpand-iterate)) +(defpackage #:losh.hash-tables + (:documentation "Utilities for operating on hash tables.") + (:export + #:mutate-hash-values)) + (defpackage #:losh.sequences (:documentation "Utilities for operating on sequences.") (:export #:prefix-sums #:frequencies + #:proportions #:group-by)) (defpackage #:losh.debugging @@ -184,19 +190,23 @@ (defpackage-inheriting #:losh (#:losh.arrays + #:losh.control-flow #:losh.debugging - #:losh.sequences #:losh.eldritch-horrors #:losh.functions + #:losh.hash-tables #:losh.iterate + #:losh.licensing #:losh.lists #:losh.math #:losh.mutation #:losh.queues #:losh.random + #:losh.sequences #:losh.weightlists - #:losh.licensing) + + ) (:use #:cl #:iterate