about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ravanan/utils.scm71
1 files changed, 69 insertions, 2 deletions
diff --git a/ravanan/utils.scm b/ravanan/utils.scm
index fbf71ed..9cc120a 100644
--- a/ravanan/utils.scm
+++ b/ravanan/utils.scm
@@ -1,5 +1,5 @@
 ;;; ravanan --- High-reproducibility CWL runner powered by Guix
-;;; Copyright © 2024, 2025 Arun Isaac <arunisaac@systemreboot.net>
+;;; Copyright © 2024–2026 Arun Isaac <arunisaac@systemreboot.net>
 ;;;
 ;;; This file is part of ravanan.
 ;;;
@@ -22,7 +22,11 @@
   #:use-module (ice-9 filesystem)
   #:use-module (ice-9 match)
   #:export (string-trim-prefix
-            load-script)
+            load-script
+            mapn
+            map2
+            foldn
+            findn)
   #:declarative? #f)
 
 (define (string-trim-prefix prefix str)
@@ -56,3 +60,66 @@ before loading script."
        ;; probably safe to ignore this warning since we use load only within a
        ;; dummy module.
        (load script-file)))))
+
+(define (mapn proc lst n)
+  "Map the procedure @var{proc} over list @var{lst} and return a list containing
+the results. @var{proc} must return @var{n} values, in which case, @var{n} lists
+are returned. For example,
+
+(mapn (lambda (n)
+        (values (expt n 2)
+                (expt n 3)))
+      (iota 5))
+=> (0 1 4 9 16)
+=> (0 1 8 27 64)"
+  (apply values
+         (match lst
+           ;; With an empty list, we cannot know the number of values
+           ;; proc would return. Hence this special case.
+           (() (make-list n '()))
+           (_
+            (apply zip
+                   (map (lambda (x)
+                          (call-with-values (cut proc x) list))
+                        lst))))))
+
+(define map2
+  (cut mapn <> <> 2))
+
+(define (foldn proc lst . inits)
+  "Apply @var{proc} to the elements of @var{lst} to build a result, and return
+that result. @var{proc} may return multiple values, in which case, an equal
+number of values are returned. Each @var{proc} call is @code{(proc element
+previous ...)} where @code{element} is an element of @var{lst}, and
+@code{(previous ...)} is the return from the previous call to @var{proc} or the
+given @var{inits} for the first call. For example,
+
+(foldn (lambda (n sum sum-of-squares)
+         (values (+ sum n)
+                 (+ sum-of-squares (expt n 2))))
+       (iota 10)
+       0 0)
+=> 45
+=> 285"
+  (apply values
+         (fold (lambda (element results)
+                 (call-with-values (cut apply proc element results) list))
+               inits
+               lst)))
+
+(define (findn pred . lists)
+  "Return the first elements of @var{lists} that satisfies the predicate @var{pred}
+and a list of @code{#f} if no such elements are found. @var{pred} must accept as
+many arguments as there are @var{lists}.
+
+(findn (lambda (x y)
+         (and (odd? x)
+              (odd? y)))
+       (list 2 3 3)
+       (list 3 5 8))
+=> 3
+=> 5"
+  (apply values
+         (or (find (cut apply pred <...>)
+                   (apply zip lists))
+             (make-list (length lists) #f))))