group-expense: init
This commit is contained in:
8
group-expense/2025.scm
Normal file
8
group-expense/2025.scm
Normal file
@@ -0,0 +1,8 @@
|
||||
(
|
||||
(laura 67.65 (lisa laura johannes kieran))
|
||||
(lisa 24 (lisa laura johannes kieran)) ; brotmuseum
|
||||
(lisa 19.98 (lisa)) ; kaffee + tabs
|
||||
(lisa 10.45 (laura)) ; honig + nüsse
|
||||
(lisa 5.99 (lisa laura johannes kieran)) ; vegane würste + nüsse
|
||||
(johannes 1.89 (lisa laura johannes kieran)) ; reeses
|
||||
)
|
||||
90
group-expense/group-expense
Executable file
90
group-expense/group-expense
Executable file
@@ -0,0 +1,90 @@
|
||||
#! /usr/bin/env -S guile --r7rs -q -s
|
||||
!#
|
||||
|
||||
(use-modules (srfi srfi-1)
|
||||
(srfi srfi-69)
|
||||
(ice-9 pretty-print))
|
||||
|
||||
(define (balance expenses)
|
||||
(define (add-expense! totals expense)
|
||||
(let* ((payer (car expense))
|
||||
(amount (cadr expense))
|
||||
(participants (caddr expense))
|
||||
(num-participants (length participants))
|
||||
(share (/ amount num-participants)))
|
||||
;; charge each participant
|
||||
(for-each
|
||||
(lambda (participant)
|
||||
(when (not (equal? participant payer))
|
||||
(hash-table-update! totals participant
|
||||
(lambda (x) (- x share))
|
||||
(lambda () 0))))
|
||||
participants)
|
||||
;; update payer
|
||||
(hash-table-update! totals payer
|
||||
(lambda (x) (+ x amount))
|
||||
(lambda () 0))))
|
||||
(define totals (make-hash-table))
|
||||
(for-each (lambda (expense)
|
||||
(add-expense! totals expense))
|
||||
expenses)
|
||||
totals)
|
||||
|
||||
; settle balances
|
||||
(define (settle balances)
|
||||
(let ((debtors (filter (lambda (p) (< (cdr p) 0))
|
||||
(hash-table->alist balances)))
|
||||
(creditors (filter (lambda (p) (> (cdr p) 0))
|
||||
(hash-table->alist balances))))
|
||||
(let loop ((debtors debtors)
|
||||
(creditors creditors)
|
||||
(result '()))
|
||||
(cond
|
||||
;; done
|
||||
((or (null? debtors) (null? creditors))
|
||||
result)
|
||||
|
||||
(else
|
||||
(let* ((debtor (car debtors))
|
||||
(creditor (car creditors))
|
||||
(dname (car debtor))
|
||||
(cname (car creditor))
|
||||
(damt (- (cdr debtor))) ; make positive
|
||||
(camt (cdr creditor))
|
||||
(amt (min damt camt)) ; settlement amount
|
||||
|
||||
;; updated remaining amounts
|
||||
(drem (- damt amt))
|
||||
(crem (- camt amt))
|
||||
|
||||
;; updated lists
|
||||
(new-debtors
|
||||
(if (= drem 0)
|
||||
(cdr debtors)
|
||||
(cons (cons dname (- drem))
|
||||
(cdr debtors))))
|
||||
|
||||
(new-creditors
|
||||
(if (= crem 0)
|
||||
(cdr creditors)
|
||||
(cons (cons cname crem)
|
||||
(cdr creditors)))))
|
||||
|
||||
(loop new-debtors
|
||||
new-creditors
|
||||
(cons (list dname cname amt) result))))))))
|
||||
|
||||
(define (load-expenses filename)
|
||||
(with-input-from-file filename
|
||||
(lambda ()
|
||||
(read)))) ; read a single S-expression
|
||||
|
||||
;; example
|
||||
(let* ((expenses-file (or (cadr (command-line))
|
||||
(error "Usage: group-expense <expenses-file>")))
|
||||
(expenses (load-expenses expenses-file))
|
||||
(expense-balance (balance expenses)))
|
||||
(pretty-print (list
|
||||
(cons 'balances (hash-table->alist expense-balance))
|
||||
(cons 'settlement (settle expense-balance))))
|
||||
(newline))
|
||||
6
group-expense/shell.nix
Normal file
6
group-expense/shell.nix
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
pkgs ? import <nixpkgs> { },
|
||||
}:
|
||||
pkgs.mkShell {
|
||||
packages = [ pkgs.guile ];
|
||||
}
|
||||
Reference in New Issue
Block a user