Improve lib for collections (#82)

This commit is contained in:
Denis Isidoro 2019-09-26 12:15:11 -03:00
parent 1b12ffa330
commit 9dce96a871
9 changed files with 190 additions and 66 deletions

70
src/coll.sh Normal file
View file

@ -0,0 +1,70 @@
#!/usr/bin/env bash
coll::new() {
for x in "$@"; do
echo "$x"
done
}
coll::first() {
head -n1
}
coll::rest() {
tail -n +2
}
coll::map() {
local -r fn="$1"
for x in $(cat); do
"$fn" "$x"
done
}
coll::filter() {
local -r pred="$1"
for x in $(cat); do
"$pred" "$x" && echo "$x" || true
done
}
coll::remove() {
local -r pred="$1"
for x in $(cat); do
"$pred" "$x" || echo "$x"
done
}
coll::add() {
cat
for x in "$@"; do
echo "$x"
done
}
coll::reverse() {
tac
}
coll::set() {
sort -u
}
# TODO: implement tailrec
coll::reduce() {
local -r fn="$1"
local state="$2"
local -r coll="$(cat)"
local -r x="$(echo "$coll" | coll::first)"
if [ -z "$x" ]; then
echo "$state"
else
local -r new_state="$("$fn" "$state" "$x")"
echo "$coll" | coll::rest | coll::reduce "$fn" "$new_state"
fi
}

View file

@ -107,7 +107,7 @@ dict::update() {
local -r input="$(cat)"
local -r value="$(echo "$input" | dict::get "$key")"
local -r updated_value="$(eval "$fn" "$value")"
local -r updated_value="$("$fn" "$value")"
echo "$input" | dict::assoc "$key" "$updated_value"
}

View file

@ -1,32 +0,0 @@
#!/usr/bin/env bash
func::list() {
for x in "$@"; do
echo "$x"
done
}
func::map() {
local -r fn="$1"
shift
for x in $(cat); do
"$fn" "$x"
done
}
func::reduce() {
local -r fn="$1"
local state="$2"
local -r coll="$(cat)"
local -r x="$(echo "$coll" | head -n1)"
if [ -z "$x" ]; then
echo "$state"
else
local -r new_state="$("$fn" "$state" "$x")"
local -r new_coll="$(echo "$coll" | tail -n +2)"
echo "$new_coll" | func::reduce "$fn" "$new_state"
fi
}

View file

@ -6,8 +6,8 @@ fi
source "${SCRIPT_DIR}/src/arg.sh"
source "${SCRIPT_DIR}/src/cheat.sh"
source "${SCRIPT_DIR}/src/coll.sh"
source "${SCRIPT_DIR}/src/dict.sh"
source "${SCRIPT_DIR}/src/func.sh"
source "${SCRIPT_DIR}/src/health.sh"
source "${SCRIPT_DIR}/src/misc.sh"
source "${SCRIPT_DIR}/src/opts.sh"

View file

@ -4,4 +4,5 @@ assert_docker_cheat() {
cheat::find | grep -q "docker.cheat"
}
test::set_suite "cheat"
test::run "We can find at least one known cheatsheet" assert_docker_cheat

109
test/coll_test.sh Normal file
View file

@ -0,0 +1,109 @@
#!/usr/bin/env bash
test::coll_equals() {
local -r actual="$(cat)"
local -r expected="$(coll::new "$@")"
echo "$actual" | test::equals "$expected"
}
inc() {
local -r x="$1"
echo $((x+1))
}
sum() {
local -r x="$1"
local -r y="$2"
echo $((x*y))
}
powers() {
local x="$1"
coll::new $((x*10)) $((x*100))
}
odd() {
local x="$1"
[ $((x%2)) -eq 1 ]
}
coll_map() {
coll::new 1 2 3 \
| coll::map inc \
| test::coll_equals 2 3 4
}
coll_flatmap() {
coll::new 1 2 3 \
| coll::map powers \
| test::coll_equals 10 100 20 200 30 300
}
coll_reduce() {
coll::new 1 2 3 \
| coll::reduce sum 10 \
| test::equals 60
}
coll_filter() {
coll::new 1 2 3 4 5 \
| coll::filter odd \
| test::coll_equals 1 3 5
}
coll_remove() {
coll::new 1 2 3 4 5 \
| coll::remove odd \
| test::coll_equals 2 4
}
coll_first() {
coll::new 1 2 3 \
| coll::first \
| test::coll_equals 1
}
coll_rest() {
coll::new 1 2 3 \
| coll::rest \
| test::coll_equals 2 3
}
coll_add() {
coll::new 1 2 3 \
| coll::add 4 5 \
| coll::add 6 7 \
| test::coll_equals 1 2 3 4 5 6 7
}
coll_concat() {
coll::new 1 2 3 \
| coll::add "$(coll::new 4 5)" \
| test::coll_equals 1 2 3 4 5
}
coll_reverse() {
coll::new 1 2 3 \
| coll::reverse \
| test::coll_equals 3 2 1
}
coll_set() {
coll::new 1 2 3 2 4 2 \
| coll::set \
| test::coll_equals 1 2 3 4
}
test::set_suite "coll"
test::run "map" coll_map
test::run "filter" coll_filter
test::run "remove" coll_remove
test::run "first" coll_first
test::run "rest" coll_rest
test::run "add" coll_add
test::run "add can be used as concat" coll_concat
test::run "reduce" coll_reduce
test::run "we can use map as flatmap" coll_flatmap
test::run "reverse" coll_reverse
test::run "set" coll_set

View file

@ -7,6 +7,7 @@ opts::eval "$@"
PASSED=0
FAILED=0
SUITE=""
test::success() {
PASSED=$((PASSED+1))
@ -19,9 +20,13 @@ test::fail() {
return
}
test::set_suite() {
SUITE="$*"
}
test::run() {
echo
log::note "$1"
log::note "${SUITE:-unknown} - ${1:-unknown}"
shift
eval "$*" && test::success || test::fail
}

View file

@ -9,10 +9,7 @@ test::map_equals() {
local -r actual="$(cat | dict::_unescape_value | sort)"
local -r expected="$(dict::new "$@" | dict::_unescape_value | sort)"
if [[ "$actual" != "$expected" ]]; then
log::error "Expected '${expected}' but got '${actual}'"
return 2
fi
echo "$actual" | test::equals "$expected"
}
dict_assoc() {
@ -103,6 +100,7 @@ dict_update() {
| test::map_equals "foo" 42 "bar" 6
}
test::set_suite "dict"
test::run "We can assoc a value" dict_assoc
test::run "We can assoc multiple values" dict_assoc_multiple
test::skip "We can assoc a nested value" dict_assoc_nested

View file

@ -1,27 +0,0 @@
#!/usr/bin/env bash
inc() {
local -r x="$1"
echo $((x+1))
}
sum() {
local -r x="$1"
local -r y="$2"
echo $((x*y))
}
func_map() {
func::list 1 2 3 \
| func::map inc \
| test::equals "$(func::list 2 3 4)"
}
func_reduce() {
func::list 1 2 3 \
| func::reduce sum 10 \
| test::equals 60
}
test::run "map works" func_map
test::run "reduce works" func_reduce