From 2a614cb04db133d12a1452ce34a58829f2701b06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=82awomir=20=C5=9Aled=C5=BA?= Date: Tue, 27 Mar 2018 23:14:57 +0200 Subject: [PATCH 01/26] [release] prepare for next development iteration --- .version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.version b/.version index 879b416..f7873fe 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -2.1 +2.2-SNAPSHOT From c1ae19f27020e722b9171a442f739019480e9eab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=82awomir=20=C5=9Aled=C5=BA?= Date: Sat, 31 Aug 2019 01:52:16 +0200 Subject: [PATCH 02/26] #1 : Shorthand notations for lambdas --- src/fun.sh | 20 ++++++++++++++++---- test/map_test.sh | 5 +++++ 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/fun.sh b/src/fun.sh index d0b3af8..ba7b33b 100755 --- a/src/fun.sh +++ b/src/fun.sh @@ -66,10 +66,22 @@ lambda() { } map() { - local x - while read x; do - echo "$x" | "$@" - done + if [[ $1 != "λ" ]] && [[ $1 != "lambda" ]]; then + + local has_dollar=$(list $@ | grep '\$' | wc -l) + + if [[ $has_dollar -ne 0 ]]; then + args=$(echo $@ | sed -e 's/\$/\$a/g') + map λ a . $args + else + map λ a . "$@"' $a' + fi + else + local x + while read x; do + echo "$x" | "$@" + done + fi } foldl() { diff --git a/test/map_test.sh b/test/map_test.sh index b34217c..5371349 100755 --- a/test/map_test.sh +++ b/test/map_test.sh @@ -30,4 +30,9 @@ testFlatMap() { assertEquals "d e h l l l o o r w" "$(list hello world | map lambda x . 'command fold -w 1 <<< $x' | sort | unlist)" } +testMapNoLambdaSyntax() { + assertEquals "1 2 3" "$(list 1 2 3 | map echo | unlist)" + assertEquals "1 is a number 2 is a number 3 is a number" "$(list 1 2 3 | map 'echo $ is a number' | unlist)" +} + . ./shunit2-init.sh \ No newline at end of file From 857a3c6109f6159cb3705663a334beb38c5e1dd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=82awomir=20=C5=9Aled=C5=BA?= Date: Sat, 31 Aug 2019 01:55:31 +0200 Subject: [PATCH 03/26] [release] prepare for next development iteration --- .version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.version b/.version index 8bbe6cf..b80651d 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -2.2 +2.3-SNAPSHOT From a23cb10ac611c1358a950f5651e744133a499f19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=82awomir=20=C5=9Aled=C5=BA?= Date: Sat, 31 Aug 2019 01:55:31 +0200 Subject: [PATCH 04/26] [release] prepare release v2.2 --- .version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.version b/.version index f7873fe..8bbe6cf 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -2.2-SNAPSHOT +2.2 From bc44865f5ae16ce0f63faba28faecf02b5e4976a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=82awomir=20=C5=9Aled=C5=BA?= Date: Sat, 31 Aug 2019 02:03:35 +0200 Subject: [PATCH 05/26] ext documentation --- README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/README.md b/README.md index 03d5783..3efb131 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,22 @@ bB sS dD eE + +$ list 1 2 3 | map echo +1 +2 +3 + +$ list 1 2 3 | map 'echo $ is a number' +1 is a number +2 is a number +3 is a number + +$ list 1 2 3 4 | map 'echo \($,$\) is a point' +(1,1) is a point +(2,2) is a point +(3,3) is a point +(4,4) is a point ``` ## *flat map* From 61e96159ec8061cd4627cd26ddb0c9d5bb401973 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=82awomir=20=C5=9Aled=C5=BA?= Date: Sat, 31 Aug 2019 02:14:44 +0200 Subject: [PATCH 06/26] [release] prepare release v2.3 --- .version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.version b/.version index b80651d..bb576db 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -2.3-SNAPSHOT +2.3 From 021477770ca325d6c994070650159c508b7184f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=82awomir=20=C5=9Aled=C5=BA?= Date: Sat, 31 Aug 2019 02:14:44 +0200 Subject: [PATCH 07/26] [release] prepare for next development iteration --- .version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.version b/.version index bb576db..f35288e 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -2.3 +2.4-SNAPSHOT From cba3556da8028ad3c06f83769b1690f1ad03d496 Mon Sep 17 00:00:00 2001 From: tpoindex Date: Wed, 11 Sep 2019 20:17:45 -0600 Subject: [PATCH 08/26] fix comma quoting in tup/tupx by quoting all commas --- src/fun.sh | 4 ++-- test/tup_test.sh | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/fun.sh b/src/fun.sh index ba7b33b..c8c4420 100755 --- a/src/fun.sh +++ b/src/fun.sh @@ -262,7 +262,7 @@ tup() { read arg tup $arg else - list "$@" | map lambda x . 'echo ${x/,/u002c}' | join , '(' ')' + list "$@" | map lambda x . 'echo ${x//,/u002c}' | join , '(' ')' fi } @@ -274,7 +274,7 @@ tupx() { else local n=$1 shift - echo "$@" | stripl '(' | stripr ')' | cut -d',' -f${n} | tr ',' '\n' | map lambda x . 'echo ${x/u002c/,}' + echo "$@" | stripl '(' | stripr ')' | cut -d',' -f${n} | tr ',' '\n' | map lambda x . 'echo ${x//u002c/,}' fi } diff --git a/test/tup_test.sh b/test/tup_test.sh index bdd5ac1..78cdeb8 100755 --- a/test/tup_test.sh +++ b/test/tup_test.sh @@ -9,6 +9,7 @@ testTupIfOneElement() { assertEquals '(")' $(tup '"') assertEquals "(')" $(tup "'") assertEquals "(u002c)" $(tup ",") + assertEquals "(u002cu002c)" $(tup ",,") assertEquals "(()" $(tup "(") assertEquals "())" $(tup ")") } @@ -38,6 +39,7 @@ testTupxIfZeroIndex() { testTupxIfSpecialChars() { assertEquals ',' "$(tup ',' | tupx 1)" + assertEquals ',,' "$(tup ',,' | tupx 1)" assertEquals '(' "$(tup '(' | tupx 1)" assertEquals ')' "$(tup ')' | tupx 1)" assertEquals '()' "$(tup '()' | tupx 1)" @@ -45,6 +47,7 @@ testTupxIfSpecialChars() { assertEquals '(' "$(tup '(' '(' | tupx 1)" assertEquals ')' "$(tup ')' ')' | tupx 1)" assertEquals ',' "$(tup 'u002c' | tupx 1)" + assertEquals ',,' "$(tup 'u002cu002c' | tupx 1)" } testTupxRange() { @@ -66,4 +69,4 @@ testTupr() { assertEquals '5' "$(tup 5 | tupr)" } -. ./shunit2-init.sh \ No newline at end of file +. ./shunit2-init.sh From 9c238777e1e3d0075a182e759dd6fc428a8e7632 Mon Sep 17 00:00:00 2001 From: tpoindex Date: Wed, 11 Sep 2019 20:58:30 -0600 Subject: [PATCH 09/26] ntup{x,l,r} - tuples that can be nested and without quoting issues, base64 encoded elements --- src/fun.sh | 30 ++++++++++++++++++++++++++++++ test/tup_test.sh | 12 ++++++++++++ 2 files changed, 42 insertions(+) diff --git a/src/fun.sh b/src/fun.sh index c8c4420..48b0894 100755 --- a/src/fun.sh +++ b/src/fun.sh @@ -286,6 +286,36 @@ tupr() { tupx 1- "$@" | last } +ntup() { + if [[ $# -eq 0 ]]; then + local arg + read arg + ntup $arg + else + list "$@" | map lambda x . 'echo "$x" | base64 --wrap=0 ; echo' | join , '(' ')' + fi +} + +ntupx() { + if [[ $# -eq 1 ]]; then + local arg + read arg + ntupx "$1" "$arg" + else + local n=$1 + shift + echo "$@" | stripl '(' | stripr ')' | cut -d',' -f${n} | tr , '\n' | map lambda x . 'echo "$x" | base64 -d' + fi +} + +ntupl() { + ntupx 1 "$@" +} + +ntupr() { + ntupx 1- "$@" | last +} + zip() { local list=$* cat - | while read x; do diff --git a/test/tup_test.sh b/test/tup_test.sh index 78cdeb8..25cd8bf 100755 --- a/test/tup_test.sh +++ b/test/tup_test.sh @@ -69,4 +69,16 @@ testTupr() { assertEquals '5' "$(tup 5 | tupr)" } +testNTup() { + assertEquals '(KFlRbz0sWWdvPSkK,Ywo=)' "$(ntup $(ntup a b) c)" + assertEquals '(YQo=,Ygo=)' "$(ntupl '(KFlRbz0sWWdvPSkK,Ywo=)')" + assertEquals 'a' "$(ntupl '(YQo=,Ygo=)')" + assertEquals 'b' "$(ntupr '(YQo=,Ygo=)')" + assertEquals 'c' "$(ntupr '(KFlRbz0sWWdvPSkK,Ywo=)')" + assertEquals 'a' "$(ntup $(ntup a b) c | ntupx 1 | ntupx 1)" + assertEquals 'b' "$(ntup $(ntup a b) c | ntupx 1 | ntupx 2)" + assertEquals 'c' "$(ntup $(ntup a b) c | ntupx 2)" + assertEquals 'a b' "$(ntup $(ntup a b) c | ntupx 1 | ntupx 1,2 | unlist)" +} + . ./shunit2-init.sh From 912b3f249116b506fae1c762d957d75c1dfe9b70 Mon Sep 17 00:00:00 2001 From: tpoindex Date: Wed, 11 Sep 2019 22:47:39 -0600 Subject: [PATCH 10/26] maybe monad and friends maybe - return a tuple of (Nothing) or (Just,value) maybemap - apply map function when maybe has a value and wrap in another maybe; else when nothing return nothing maybevalue - return value of maybe; else when nothing return optional default args --- src/fun.sh | 40 ++++++++++++++++++++++++++++++++++++++++ test/maybe_test.sh | 29 +++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100755 test/maybe_test.sh diff --git a/src/fun.sh b/src/fun.sh index 48b0894..e1ad6d9 100755 --- a/src/fun.sh +++ b/src/fun.sh @@ -358,3 +358,43 @@ call() { local args=$@ tup $f $args } + +maybe() { + if [[ $# -eq 0 ]]; then + local arg + read arg + maybe "$arg" + else + local x="$*" + local value=$(echo $x | strip) + if [[ ${#value} -eq 0 ]]; then + tup Nothing + else + tup Just "$value" + fi + fi +} + +maybemap() { + local x + read x + if [[ $(tupl $x) = "Nothing" ]]; then + echo $x + else + local y=$(tupr "$x") + local r=$(echo "$y" | map "$@") + maybe "$r" + fi +} + +maybevalue() { + local default="$*" + local x + read x + if [[ $(tupl $x) = "Nothing" ]]; then + echo "$default" + else + echo $(tupr $x) + fi +} + diff --git a/test/maybe_test.sh b/test/maybe_test.sh new file mode 100755 index 0000000..1003179 --- /dev/null +++ b/test/maybe_test.sh @@ -0,0 +1,29 @@ +#! /bin/bash + +testMaybe() { + assertEquals '(Just,1)' "$(maybe 1)" + assertEquals '(Just,1)' "$(echo 1 | maybe)" + assertEquals '(Nothing)' "$(maybe '')" + assertEquals '(Nothing)' "$(maybe ' ')" + assertEquals '(Nothing)' "$(maybe ' ' ' ' ' ')" + assertEquals '(Nothing)' "$(echo | maybe)" + assertEquals '(Just,1 2 3)' "$(maybe 1 2 3)" + assertEquals '(Just,1 2 3)' "$(echo 1 2 3 | maybe)" +} + +testMaybemap() { + assertEquals '(Just,3)' "$(echo 1 | maybe | maybemap lambda a . 'echo $(( a + 1 ))' | maybemap lambda a . 'echo $(( a + 1 ))')" + assertEquals '(Nothing)' "$(echo | maybe | maybemap lambda a . 'echo $(( a + 1 ))' | maybemap lambda a . 'echo $(( a + 1 ))')" + + assertEquals '(Nothing)' "$(echo 1 | maybe | maybemap lambda a . 'echo $(( a + 1 ))' | maybemap lambda a . 'echo')" + assertEquals '(Nothing)' "$(echo 1 | maybe | maybemap lambda a . 'echo $(( a + 1 ))' | maybemap lambda a . 'echo' | maybemap lambda a . 'echo $(( a + 1 ))')" +} + +testMaybevalue() { + assertEquals 3 "$(echo 1 | maybe | maybemap lambda a . 'echo $(( a + 1 ))' | maybemap lambda a . 'echo $(( a + 1 ))' | maybevalue 0)" + assertEquals 0 "$(echo | maybe | maybemap lambda a . 'echo $(( a + 1 ))' | maybemap lambda a . 'echo $(( a + 1 ))' | maybevalue 0)" + assertEquals 'a b c' "$(echo | maybe | maybemap lambda a . 'echo $(( a + 1 ))' | maybemap lambda a . 'echo $(( a + 1 ))' | maybevalue a b c)" +} + + +. ./shunit2-init.sh From 7b6531a74e6c80ebaf798d44dc68022acb9159ea Mon Sep 17 00:00:00 2001 From: tpoindex Date: Wed, 11 Sep 2019 23:46:47 -0600 Subject: [PATCH 11/26] fluent predicates for filtering --- src/fun.sh | 37 +++++++++++++++++++++++++++++ test/predicates_test.sh | 52 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+) create mode 100755 test/predicates_test.sh diff --git a/src/fun.sh b/src/fun.sh index e1ad6d9..5d44542 100755 --- a/src/fun.sh +++ b/src/fun.sh @@ -398,3 +398,40 @@ maybevalue() { fi } + +# commonly used predicates for filter +# e.g. list 1 a 2 b 3 c | filter lambda x . 'isint $x' + +# inverse another test, e.g. "not isint $x" +not() { + local r=$("$@" 2>/dev/null) + $r && ret false || ret true +} + +isint() { + [ "$1" -eq "$1" ] 2>/dev/null && ret true || ret false +} + +isempty() { + [ -z "$1" ] && ret true || ret false +} + +isfile() { + [ -f "$1" ] && ret true || ret false +} + +isnonzerofile() { + [ -s "$1" ] && ret true || ret false +} + +isreadable() { + [ -r "$1" ] && ret true || ret false +} + +iswritable() { + [ -w "$1" ] && ret true || ret false +} + +isdir() { + [ -d "$1" ] && ret true || ret false +} diff --git a/test/predicates_test.sh b/test/predicates_test.sh new file mode 100755 index 0000000..79bff20 --- /dev/null +++ b/test/predicates_test.sh @@ -0,0 +1,52 @@ +#! /bin/bash + +testIsint() { + assertEquals 'true' $(isint 1) + assertEquals 'true' $(isint -1) + assertEquals 'false' $(isint a) + assertEquals 'false' $(isint "") + assertEquals '1 2 3 4 5' "$(list 1 a 2 b 3 c 4 d 5 e | filter lambda x . 'isint $x' | unlist )" + assertEquals '1 2' "$(list 1 a 2 b 3 c 4 d 5 e | filter lambda x . '($(isint $x) && [[ $x -le 2 ]] && ret true) || ret false ' | unlist )" + + assertEquals 'false' $(not isint 1) + assertEquals 'true' $(not isint a) +} + +testIsempty() { + assertEquals 'true' $(isempty "") + assertEquals 'false' $(isempty a) + + assertEquals 'true' $(not isempty a) + assertEquals 'false' $(not isempty "") +} + +testIsfile() { + f=$(mktemp) + + assertEquals 'true' $(isfile $f) + assertEquals 'false' $(isfile $f.xxx) + assertEquals 'false' $(isfile "") + assertEquals 'true' $(not isfile $f.xxx) + + assertEquals 'false' $(isnonzerofile $f) + echo hello world >$f + assertEquals 'true' $(isnonzerofile $f) + + assertEquals 'true' $(iswritable $f) + chmod 400 $f + assertEquals 'false' $(iswritable $f) + + assertEquals 'true' $(isreadable $f) + chmod 200 $f + assertEquals 'false' $(isreadable $f) + + chmod 600 $f + rm $f +} + +testIsdir() { + assertEquals 'true' $(isdir .) + assertEquals 'false' $(isdir sir_not_appearing_in_this_film) +} + +. ./shunit2-init.sh From 1ead6d0b87b0ba311f19ef99bab9d6128898145c Mon Sep 17 00:00:00 2001 From: tpoindex Date: Wed, 11 Sep 2019 23:53:56 -0600 Subject: [PATCH 12/26] avoid collisions with common /usr/bin/ commands rename head, tail, zip to lhead, ltail, and lzip to avoid collisions with command unix commands --- src/fun.sh | 6 +++--- test/head_test.sh | 16 ++++++++-------- test/tail_test.sh | 14 +++++++------- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/fun.sh b/src/fun.sh index 5d44542..08f3bc1 100755 --- a/src/fun.sh +++ b/src/fun.sh @@ -8,11 +8,11 @@ take() { command head -n ${1} } -tail() { +ltail() { drop 1 } -head() { +lhead() { take 1 } @@ -316,7 +316,7 @@ ntupr() { ntupx 1- "$@" | last } -zip() { +lzip() { local list=$* cat - | while read x; do y=$(list $list | take 1) diff --git a/test/head_test.sh b/test/head_test.sh index 3ea2a7f..90ea201 100755 --- a/test/head_test.sh +++ b/test/head_test.sh @@ -1,16 +1,16 @@ #! /bin/bash -testHeadFromList() { - assertEquals 1 $(list {1..10} | head) - assertEquals 5 $(list 5 6 7 | head) +testLHeadFromList() { + assertEquals 1 $(list {1..10} | lhead) + assertEquals 5 $(list 5 6 7 | lhead) } -testHeadFromOneElementList() { - assertEquals 1 $(list 1 | head) +testLHeadFromOneElementList() { + assertEquals 1 $(list 1 | lhead) } -testHeadFromEmptyList() { - assertEquals "" "$(list | head)" +testLHeadFromEmptyList() { + assertEquals "" "$(list | lhead)" } -. ./shunit2-init.sh \ No newline at end of file +. ./shunit2-init.sh diff --git a/test/tail_test.sh b/test/tail_test.sh index 57971ad..b2b19f5 100755 --- a/test/tail_test.sh +++ b/test/tail_test.sh @@ -1,15 +1,15 @@ #! /bin/bash -testTailFrom10() { - assertEquals "2 3 4 5 6 7 8 9 10" "$(list {1..10} | tail | unlist)" +testLTailFrom10() { + assertEquals "2 3 4 5 6 7 8 9 10" "$(list {1..10} | ltail | unlist)" } -testTailFromOneElementList() { - assertEquals "" "$(list 1 | tail)" +testLTailFromOneElementList() { + assertEquals "" "$(list 1 | ltail)" } -testTailFromEmptyList() { - assertEquals "" "$(list | tail)" +testLTailFromEmptyList() { + assertEquals "" "$(list | ltail)" } -. ./shunit2-init.sh \ No newline at end of file +. ./shunit2-init.sh From febedd4c5c4fc307525dcae6e7c93098a3544f9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=82awomir=20=C5=9Aled=C5=BA?= Date: Sat, 14 Sep 2019 19:54:57 +0200 Subject: [PATCH 13/26] Bumping version --- .version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.version b/.version index bb576db..6b4950e 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -2.3 +2.4 From b40adbb643b829c30b69c4c3decc80daff72dd41 Mon Sep 17 00:00:00 2001 From: tpoindex Date: Wed, 11 Sep 2019 20:17:45 -0600 Subject: [PATCH 14/26] fix comma quoting in tup/tupx by quoting all commas --- src/fun.sh | 4 ++-- test/tup_test.sh | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/fun.sh b/src/fun.sh index ba7b33b..c8c4420 100755 --- a/src/fun.sh +++ b/src/fun.sh @@ -262,7 +262,7 @@ tup() { read arg tup $arg else - list "$@" | map lambda x . 'echo ${x/,/u002c}' | join , '(' ')' + list "$@" | map lambda x . 'echo ${x//,/u002c}' | join , '(' ')' fi } @@ -274,7 +274,7 @@ tupx() { else local n=$1 shift - echo "$@" | stripl '(' | stripr ')' | cut -d',' -f${n} | tr ',' '\n' | map lambda x . 'echo ${x/u002c/,}' + echo "$@" | stripl '(' | stripr ')' | cut -d',' -f${n} | tr ',' '\n' | map lambda x . 'echo ${x//u002c/,}' fi } diff --git a/test/tup_test.sh b/test/tup_test.sh index bdd5ac1..78cdeb8 100755 --- a/test/tup_test.sh +++ b/test/tup_test.sh @@ -9,6 +9,7 @@ testTupIfOneElement() { assertEquals '(")' $(tup '"') assertEquals "(')" $(tup "'") assertEquals "(u002c)" $(tup ",") + assertEquals "(u002cu002c)" $(tup ",,") assertEquals "(()" $(tup "(") assertEquals "())" $(tup ")") } @@ -38,6 +39,7 @@ testTupxIfZeroIndex() { testTupxIfSpecialChars() { assertEquals ',' "$(tup ',' | tupx 1)" + assertEquals ',,' "$(tup ',,' | tupx 1)" assertEquals '(' "$(tup '(' | tupx 1)" assertEquals ')' "$(tup ')' | tupx 1)" assertEquals '()' "$(tup '()' | tupx 1)" @@ -45,6 +47,7 @@ testTupxIfSpecialChars() { assertEquals '(' "$(tup '(' '(' | tupx 1)" assertEquals ')' "$(tup ')' ')' | tupx 1)" assertEquals ',' "$(tup 'u002c' | tupx 1)" + assertEquals ',,' "$(tup 'u002cu002c' | tupx 1)" } testTupxRange() { @@ -66,4 +69,4 @@ testTupr() { assertEquals '5' "$(tup 5 | tupr)" } -. ./shunit2-init.sh \ No newline at end of file +. ./shunit2-init.sh From 7736a293a02b28c208f108176a043a5a66883927 Mon Sep 17 00:00:00 2001 From: tpoindex Date: Wed, 11 Sep 2019 20:58:30 -0600 Subject: [PATCH 15/26] ntup{x,l,r} - tuples that can be nested and without quoting issues, base64 encoded elements --- src/fun.sh | 30 ++++++++++++++++++++++++++++++ test/tup_test.sh | 12 ++++++++++++ 2 files changed, 42 insertions(+) diff --git a/src/fun.sh b/src/fun.sh index c8c4420..48b0894 100755 --- a/src/fun.sh +++ b/src/fun.sh @@ -286,6 +286,36 @@ tupr() { tupx 1- "$@" | last } +ntup() { + if [[ $# -eq 0 ]]; then + local arg + read arg + ntup $arg + else + list "$@" | map lambda x . 'echo "$x" | base64 --wrap=0 ; echo' | join , '(' ')' + fi +} + +ntupx() { + if [[ $# -eq 1 ]]; then + local arg + read arg + ntupx "$1" "$arg" + else + local n=$1 + shift + echo "$@" | stripl '(' | stripr ')' | cut -d',' -f${n} | tr , '\n' | map lambda x . 'echo "$x" | base64 -d' + fi +} + +ntupl() { + ntupx 1 "$@" +} + +ntupr() { + ntupx 1- "$@" | last +} + zip() { local list=$* cat - | while read x; do diff --git a/test/tup_test.sh b/test/tup_test.sh index 78cdeb8..25cd8bf 100755 --- a/test/tup_test.sh +++ b/test/tup_test.sh @@ -69,4 +69,16 @@ testTupr() { assertEquals '5' "$(tup 5 | tupr)" } +testNTup() { + assertEquals '(KFlRbz0sWWdvPSkK,Ywo=)' "$(ntup $(ntup a b) c)" + assertEquals '(YQo=,Ygo=)' "$(ntupl '(KFlRbz0sWWdvPSkK,Ywo=)')" + assertEquals 'a' "$(ntupl '(YQo=,Ygo=)')" + assertEquals 'b' "$(ntupr '(YQo=,Ygo=)')" + assertEquals 'c' "$(ntupr '(KFlRbz0sWWdvPSkK,Ywo=)')" + assertEquals 'a' "$(ntup $(ntup a b) c | ntupx 1 | ntupx 1)" + assertEquals 'b' "$(ntup $(ntup a b) c | ntupx 1 | ntupx 2)" + assertEquals 'c' "$(ntup $(ntup a b) c | ntupx 2)" + assertEquals 'a b' "$(ntup $(ntup a b) c | ntupx 1 | ntupx 1,2 | unlist)" +} + . ./shunit2-init.sh From b19ab5180b28ca6e5dcab0dce68404c19f354dca Mon Sep 17 00:00:00 2001 From: tpoindex Date: Wed, 11 Sep 2019 22:47:39 -0600 Subject: [PATCH 16/26] maybe monad and friends maybe - return a tuple of (Nothing) or (Just,value) maybemap - apply map function when maybe has a value and wrap in another maybe; else when nothing return nothing maybevalue - return value of maybe; else when nothing return optional default args --- src/fun.sh | 40 ++++++++++++++++++++++++++++++++++++++++ test/maybe_test.sh | 29 +++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100755 test/maybe_test.sh diff --git a/src/fun.sh b/src/fun.sh index 48b0894..e1ad6d9 100755 --- a/src/fun.sh +++ b/src/fun.sh @@ -358,3 +358,43 @@ call() { local args=$@ tup $f $args } + +maybe() { + if [[ $# -eq 0 ]]; then + local arg + read arg + maybe "$arg" + else + local x="$*" + local value=$(echo $x | strip) + if [[ ${#value} -eq 0 ]]; then + tup Nothing + else + tup Just "$value" + fi + fi +} + +maybemap() { + local x + read x + if [[ $(tupl $x) = "Nothing" ]]; then + echo $x + else + local y=$(tupr "$x") + local r=$(echo "$y" | map "$@") + maybe "$r" + fi +} + +maybevalue() { + local default="$*" + local x + read x + if [[ $(tupl $x) = "Nothing" ]]; then + echo "$default" + else + echo $(tupr $x) + fi +} + diff --git a/test/maybe_test.sh b/test/maybe_test.sh new file mode 100755 index 0000000..1003179 --- /dev/null +++ b/test/maybe_test.sh @@ -0,0 +1,29 @@ +#! /bin/bash + +testMaybe() { + assertEquals '(Just,1)' "$(maybe 1)" + assertEquals '(Just,1)' "$(echo 1 | maybe)" + assertEquals '(Nothing)' "$(maybe '')" + assertEquals '(Nothing)' "$(maybe ' ')" + assertEquals '(Nothing)' "$(maybe ' ' ' ' ' ')" + assertEquals '(Nothing)' "$(echo | maybe)" + assertEquals '(Just,1 2 3)' "$(maybe 1 2 3)" + assertEquals '(Just,1 2 3)' "$(echo 1 2 3 | maybe)" +} + +testMaybemap() { + assertEquals '(Just,3)' "$(echo 1 | maybe | maybemap lambda a . 'echo $(( a + 1 ))' | maybemap lambda a . 'echo $(( a + 1 ))')" + assertEquals '(Nothing)' "$(echo | maybe | maybemap lambda a . 'echo $(( a + 1 ))' | maybemap lambda a . 'echo $(( a + 1 ))')" + + assertEquals '(Nothing)' "$(echo 1 | maybe | maybemap lambda a . 'echo $(( a + 1 ))' | maybemap lambda a . 'echo')" + assertEquals '(Nothing)' "$(echo 1 | maybe | maybemap lambda a . 'echo $(( a + 1 ))' | maybemap lambda a . 'echo' | maybemap lambda a . 'echo $(( a + 1 ))')" +} + +testMaybevalue() { + assertEquals 3 "$(echo 1 | maybe | maybemap lambda a . 'echo $(( a + 1 ))' | maybemap lambda a . 'echo $(( a + 1 ))' | maybevalue 0)" + assertEquals 0 "$(echo | maybe | maybemap lambda a . 'echo $(( a + 1 ))' | maybemap lambda a . 'echo $(( a + 1 ))' | maybevalue 0)" + assertEquals 'a b c' "$(echo | maybe | maybemap lambda a . 'echo $(( a + 1 ))' | maybemap lambda a . 'echo $(( a + 1 ))' | maybevalue a b c)" +} + + +. ./shunit2-init.sh From 44dbdd3fbc71b015dc1c40cad46cde3572b3d62f Mon Sep 17 00:00:00 2001 From: tpoindex Date: Wed, 11 Sep 2019 23:46:47 -0600 Subject: [PATCH 17/26] fluent predicates for filtering --- src/fun.sh | 37 +++++++++++++++++++++++++++++ test/predicates_test.sh | 52 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+) create mode 100755 test/predicates_test.sh diff --git a/src/fun.sh b/src/fun.sh index e1ad6d9..5d44542 100755 --- a/src/fun.sh +++ b/src/fun.sh @@ -398,3 +398,40 @@ maybevalue() { fi } + +# commonly used predicates for filter +# e.g. list 1 a 2 b 3 c | filter lambda x . 'isint $x' + +# inverse another test, e.g. "not isint $x" +not() { + local r=$("$@" 2>/dev/null) + $r && ret false || ret true +} + +isint() { + [ "$1" -eq "$1" ] 2>/dev/null && ret true || ret false +} + +isempty() { + [ -z "$1" ] && ret true || ret false +} + +isfile() { + [ -f "$1" ] && ret true || ret false +} + +isnonzerofile() { + [ -s "$1" ] && ret true || ret false +} + +isreadable() { + [ -r "$1" ] && ret true || ret false +} + +iswritable() { + [ -w "$1" ] && ret true || ret false +} + +isdir() { + [ -d "$1" ] && ret true || ret false +} diff --git a/test/predicates_test.sh b/test/predicates_test.sh new file mode 100755 index 0000000..79bff20 --- /dev/null +++ b/test/predicates_test.sh @@ -0,0 +1,52 @@ +#! /bin/bash + +testIsint() { + assertEquals 'true' $(isint 1) + assertEquals 'true' $(isint -1) + assertEquals 'false' $(isint a) + assertEquals 'false' $(isint "") + assertEquals '1 2 3 4 5' "$(list 1 a 2 b 3 c 4 d 5 e | filter lambda x . 'isint $x' | unlist )" + assertEquals '1 2' "$(list 1 a 2 b 3 c 4 d 5 e | filter lambda x . '($(isint $x) && [[ $x -le 2 ]] && ret true) || ret false ' | unlist )" + + assertEquals 'false' $(not isint 1) + assertEquals 'true' $(not isint a) +} + +testIsempty() { + assertEquals 'true' $(isempty "") + assertEquals 'false' $(isempty a) + + assertEquals 'true' $(not isempty a) + assertEquals 'false' $(not isempty "") +} + +testIsfile() { + f=$(mktemp) + + assertEquals 'true' $(isfile $f) + assertEquals 'false' $(isfile $f.xxx) + assertEquals 'false' $(isfile "") + assertEquals 'true' $(not isfile $f.xxx) + + assertEquals 'false' $(isnonzerofile $f) + echo hello world >$f + assertEquals 'true' $(isnonzerofile $f) + + assertEquals 'true' $(iswritable $f) + chmod 400 $f + assertEquals 'false' $(iswritable $f) + + assertEquals 'true' $(isreadable $f) + chmod 200 $f + assertEquals 'false' $(isreadable $f) + + chmod 600 $f + rm $f +} + +testIsdir() { + assertEquals 'true' $(isdir .) + assertEquals 'false' $(isdir sir_not_appearing_in_this_film) +} + +. ./shunit2-init.sh From 1b234ff597d4eceb5774c81a8b3befc567c20692 Mon Sep 17 00:00:00 2001 From: tpoindex Date: Wed, 11 Sep 2019 23:53:56 -0600 Subject: [PATCH 18/26] avoid collisions with common /usr/bin/ commands rename head, tail, zip to lhead, ltail, and lzip to avoid collisions with command unix commands --- src/fun.sh | 6 +++--- test/head_test.sh | 16 ++++++++-------- test/tail_test.sh | 14 +++++++------- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/fun.sh b/src/fun.sh index 5d44542..08f3bc1 100755 --- a/src/fun.sh +++ b/src/fun.sh @@ -8,11 +8,11 @@ take() { command head -n ${1} } -tail() { +ltail() { drop 1 } -head() { +lhead() { take 1 } @@ -316,7 +316,7 @@ ntupr() { ntupx 1- "$@" | last } -zip() { +lzip() { local list=$* cat - | while read x; do y=$(list $list | take 1) diff --git a/test/head_test.sh b/test/head_test.sh index 3ea2a7f..90ea201 100755 --- a/test/head_test.sh +++ b/test/head_test.sh @@ -1,16 +1,16 @@ #! /bin/bash -testHeadFromList() { - assertEquals 1 $(list {1..10} | head) - assertEquals 5 $(list 5 6 7 | head) +testLHeadFromList() { + assertEquals 1 $(list {1..10} | lhead) + assertEquals 5 $(list 5 6 7 | lhead) } -testHeadFromOneElementList() { - assertEquals 1 $(list 1 | head) +testLHeadFromOneElementList() { + assertEquals 1 $(list 1 | lhead) } -testHeadFromEmptyList() { - assertEquals "" "$(list | head)" +testLHeadFromEmptyList() { + assertEquals "" "$(list | lhead)" } -. ./shunit2-init.sh \ No newline at end of file +. ./shunit2-init.sh diff --git a/test/tail_test.sh b/test/tail_test.sh index 57971ad..b2b19f5 100755 --- a/test/tail_test.sh +++ b/test/tail_test.sh @@ -1,15 +1,15 @@ #! /bin/bash -testTailFrom10() { - assertEquals "2 3 4 5 6 7 8 9 10" "$(list {1..10} | tail | unlist)" +testLTailFrom10() { + assertEquals "2 3 4 5 6 7 8 9 10" "$(list {1..10} | ltail | unlist)" } -testTailFromOneElementList() { - assertEquals "" "$(list 1 | tail)" +testLTailFromOneElementList() { + assertEquals "" "$(list 1 | ltail)" } -testTailFromEmptyList() { - assertEquals "" "$(list | tail)" +testLTailFromEmptyList() { + assertEquals "" "$(list | ltail)" } -. ./shunit2-init.sh \ No newline at end of file +. ./shunit2-init.sh From 540840e9d28b567ddb9ccfa145ccebb06c3cd43c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=82awomir=20=C5=9Aled=C5=BA?= Date: Sat, 14 Sep 2019 20:23:38 +0200 Subject: [PATCH 19/26] Manually bumping version --- .version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.version b/.version index f35288e..9dcc69b 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -2.4-SNAPSHOT +2.5-SNAPSHOT From b590285ec985bdaf97b0b4c1e32ac87870cd0c6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=82awomir=20=C5=9Aled=C5=BA?= Date: Tue, 1 Oct 2019 00:02:34 +0200 Subject: [PATCH 20/26] Fixing examples & documentation + adding example of usage to documentation. --- README.md | 97 +++++++++++++++++++++++++++++++++++++++------ examples/example.sh | 18 ++++----- 2 files changed, 93 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 3efb131..e44672b 100644 --- a/README.md +++ b/README.md @@ -14,13 +14,17 @@ seq 1 4 | sum # Functions overview ||||||| |------|------|------|------|------|------| -|**plus**|**append**|**buff**|**curry**|**div**|**drop**| -|**factorial**|**filter**|**foldl**|**foldr**|**head**|**join**| -|**lambda**|**last**|**list**|**map**|**mod**|**mul**| -|**prepend**|**product**|**ret**|**revers_str**|**revers**|**scanl**| -|**splitc**|**strip**|**sub**|**sum**|**tail**|**take**| -|**catch**|**try**|**tupl**|**tupr**|**tup**|**tupx**| -|**unlist**|**zip**|**λ**|**with_trampoline**|**res**|**call**| +|**append**|**buff**|**call**|**catch**|**curry**|**div**| +|**drop**|**dropw**|**factorial**|**filter**|**foldl**|**foldr**| +|**isint**|**isempty**|**isfile**|**isnonzerofile**|**isreadable**|**iswritable**| +|**isdir**|**join**|**lambda**|**last**|**lhead**|**list**| +|**ltail**|**lzip**|**map**|**maybe**|**maybemap**|**maybevalue**| +|**mod**|**mul**|**not**|**ntup**|**ntupl**|**ntupr**| +|**ntupx**|**peek**|**plus**|**prepend**|**product**|**ret**| +|**res**|**revers**|**revers_str**|**scanl**|**splitc**|**strip**| +|**stripl**|**stripr**|**sub**|**sum**|**take**|**try**| +|**tup**|**tupl**|**tupr**|**tupx**|**unlist**|**λ**| +|**with_trampoline**| ## *list/unlist* @@ -34,17 +38,17 @@ $ list 1 2 3 4 5 | unlist 1 2 3 4 5 ``` -## *take/drop/tail/head/last* +## *take/drop/ltail/lhead/last* ```bash $ list 1 2 3 4 | drop 2 3 4 -$ list 1 2 3 4 5 | head +$ list 1 2 3 4 5 | lhead 1 -$ list 1 2 3 4 | tail +$ list 1 2 3 4 | ltail 2 3 4 @@ -190,6 +194,30 @@ one 2 ``` +## *ntup/ntupx/ntupl/ntupr* + +```bash +$ ntup a 1 b 2 c 3 +(YQo=,MQo=,Ygo=,Mgo=,Ywo=,Mwo=) + +$ echo '(YQo=,MQo=,Ygo=,Mgo=,Ywo=,Mwo=)' | ntupx 3 +b + +$ ntup 'foo bar' 1 one 1 +(Zm9vIGJhcgo=,MQo=,b25lCg==,MQo=) + +$ echo '(Zm9vIGJhcgo=,MQo=,b25lCg==,MQo=)' | ntupx 1 +foo bar +``` + +```bash +$ ntupl $(ntup 'foo bar' 1 one 2) +foo bar + +$ ntupr $(ntup 'foo bar' 1 one 2) +2 +``` + ## *buff* ```bash @@ -205,10 +233,10 @@ $ seq 1 10 | buff λ a b c d e . 'echo $(($a + $b + $c + $d + $e))' 40 ``` -## *zip* +## *lzip* ```bash -$ list a b c d e f | zip $(seq 1 10) +$ list a b c d e f | lzip $(seq 1 10) (a,1) (b,2) (c,3) @@ -218,7 +246,7 @@ $ list a b c d e f | zip $(seq 1 10) ``` ```bash -$ list a b c d e f | zip $(seq 1 10) | last | tupr +$ list a b c d e f | lzip $(seq 1 10) | last | tupr 6 ``` @@ -244,6 +272,49 @@ $ seq 1 3 | map λ a . 'inc $a' 4 ``` +## *peek* + +```bash +$ list 1 2 3 \ + | peek lambda a . echo 'dbg a : $a' \ + | map lambda a . 'mul $a 2' \ + | peek lambda a . echo 'dbg b : $a' \ + | sum + +dbg a : 1 +dbg a : 2 +dbg a : 3 +dbg b : 2 +dbg b : 4 +dbg b : 6 +12 +``` + +```bash +$ a=$(seq 1 4 | peek lambda a . echo 'dbg: $a' | sum) + +dbg: 1 +dbg: 2 +dbg: 3 +dbg: 4 + +$ echo $a + +10 +``` + +## *maybe/maybemap/maybevalue* + +TODO + +## *not/isint/isempty* + +TODO + +## *isfile/isnonzerofile/isreadable/iswritable/isdir* + +TODO + ## *try/catch* ```bash diff --git a/examples/example.sh b/examples/example.sh index 80cb792..9bcc29f 100755 --- a/examples/example.sh +++ b/examples/example.sh @@ -1,26 +1,25 @@ #!/bin/bash - source ../src/fun.sh seq 1 4 | sum seq 1 4 | product factorial 4 -seq 1 4 | scanl lambda a b . 'echo $(add $a $b)' +seq 1 4 | scanl lambda a b . 'echo $(plus $a $b)' echo map mul seq 1 4 | map lambda a . 'echo $(mul $a 2)' echo map sub seq 1 4 | map lambda a . 'echo $(sub $a 2)' -echo map add -seq 1 4 | map lambda a . 'echo $(add $a 2)' +echo map plsu +seq 1 4 | map lambda a . 'echo $(plus $a 2)' echo map div seq 1 4 | map lambda a . 'echo $(div $a 2)' echo map mod seq 1 4 | map lambda a . 'echo $(mod $a 2)' echo 'list & head' -list 1 2 3 4 5 | head +list 1 2 3 4 5 | lhead list {1..2} | append {3..4} | prepend {99..102} list {1..2} | unlist -list {1..10} | head +list {1..10} | lhead list {1..10} | drop 7 list {1..10} | take 3 list {1..10} | last @@ -83,10 +82,10 @@ seq 1 10 | buff lambda a b . 'echo $(($a + $b))' echo 'XX' seq 1 10 | buff lambda a b c d e . 'echo $(($a + $b + $c + $d + $e))' -list a b c d e f | zip $(seq 1 10) +list a b c d e f | lzip $(seq 1 10) echo -list a b c d e f | zip $(seq 1 10) | last | tupr +list a b c d e f | lzip $(seq 1 10) | last | tupr arg='[key1=value1,key2=value2,key3=value3]' get() { @@ -155,5 +154,6 @@ echo Factorial test time factorial 30 time factorial_trampoline 30 -time factorial 60 +# would be error +#time factorial 60 time factorial_trampoline 60 \ No newline at end of file From 67c88a5b3ff35a92050ead04ec5f0555ca2bf11f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=82awomir=20=C5=9Aled=C5=BA?= Date: Tue, 1 Oct 2019 00:24:56 +0200 Subject: [PATCH 21/26] more docs --- README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/README.md b/README.md index e44672b..f99ee1b 100644 --- a/README.md +++ b/README.md @@ -431,6 +431,21 @@ processNames adam monika s slawek d daniel Bartek j k Adam,Monika,Slawek,Daniel,Bartek ``` +# Running tests + +```bash +cd test +./test_runner +``` + +# Contribution guidelines + +Feel free to ask questions in chat, open issues, or contribute by creating pull requests. + +In order to create a pull request +* checkout develop branch +* introduce your changes +* submit pull request # Resources * [Inspiration](https://quasimal.com/posts/2012-05-21-funsh.html) From 9840e94a5d9ccb6396e8e182e8c765b86765a52e Mon Sep 17 00:00:00 2001 From: Slawomir Sledz Date: Tue, 1 Oct 2019 00:34:01 +0200 Subject: [PATCH 22/26] Create LICENSE --- LICENSE | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..4779b34 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Sławomir Śledź + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From 5e1cf528400f1b252811383ead01aa06f56aeab4 Mon Sep 17 00:00:00 2001 From: Tom Poindexter Date: Wed, 2 Oct 2019 17:54:48 -0600 Subject: [PATCH 23/26] Add docs for ntup, maybe, is* predicates Updated ntup docs to show nested tuples, and comma safe elements. Added docs for maybe and is* predicates. --- README.md | 103 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 91 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index f99ee1b..8d6aa66 100644 --- a/README.md +++ b/README.md @@ -197,17 +197,17 @@ one ## *ntup/ntupx/ntupl/ntupr* ```bash -$ ntup a 1 b 2 c 3 -(YQo=,MQo=,Ygo=,Mgo=,Ywo=,Mwo=) +$ ntup tuples that $(ntup safely nest) +(dHVwbGVzCg==,dGhhdAo=,KGMyRm1aV3g1Q2c9PSxibVZ6ZEFvPSkK) -$ echo '(YQo=,MQo=,Ygo=,Mgo=,Ywo=,Mwo=)' | ntupx 3 -b +echo '(dHVwbGVzCg==,dGhhdAo=,KGMyRm1aV3g1Q2c9PSxibVZ6ZEFvPSkK)' | ntupx 3 | ntupr +nest -$ ntup 'foo bar' 1 one 1 -(Zm9vIGJhcgo=,MQo=,b25lCg==,MQo=) +$ ntup 'foo,bar' 1 one 1 +(Zm9vLGJhcgo=,MQo=,b25lCg==,MQo=) -$ echo '(Zm9vIGJhcgo=,MQo=,b25lCg==,MQo=)' | ntupx 1 -foo bar +$ echo '(Zm9vLGJhcgo=,MQo=,b25lCg==,MQo=)' | ntupx 1 +foo,bar ``` ```bash @@ -305,15 +305,94 @@ $ echo $a ## *maybe/maybemap/maybevalue* -TODO +```bash +$ list Hello | maybe +(Just,Hello) + +$ list " " | maybe +(Nothing) + +$ list Hello | maybe | maybemap λ a . 'tr oH Oh <<<$a' +(Just,hellO) + +$ list " " | maybe | maybemap λ a . 'tr oH Oh <<<$a' +(Nothing) + +$ echo bash-fun rocks | maybe | maybevalue DEFAULT +bash-fun rocks + +$ echo | maybe | maybevalue DEFAULT +DEFAULT + +``` ## *not/isint/isempty* -TODO +```bash +$ isint 42 +true + +$ list blah | isint +false + +$ not true +false + +$ not isint 777 +false + +$ list 1 2 "" c d 6 | filter λ a . 'isint $a' +1 +2 +6 + +$ list 1 2 "" c d 6 | filter λ a . 'not isempty $a' +1 +2 +c +d +6 +``` ## *isfile/isnonzerofile/isreadable/iswritable/isdir* -TODO +```bash +$ touch /tmp/foo + +$ isfile /tmp/foo +true + +$ not iswritable / +true + +$ files="/etc/passwd /etc/sudoers /tmp /tmp/foo /no_such_file" + +$ list $files | filter λ a . 'isfile $a' +/etc/passwd +/etc/sudoers +/tmp/foo + +$ list $files | filter λ a . 'isdir $a' +/tmp + +$ list $files | filter λ a . 'isreadable $a' +/etc/passwd +/tmp +/tmp/foo + +$ list $files | filter λ a . 'iswritable $a' +/tmp +/tmp/foo + +$ list $files | filter λ a . 'isnonzerofile $a' +/etc/passwd +/etc/sudoers +/tmp + +$ list $files | filter λ a . 'not isfile $a' +/tmp +/no_such_file +``` ## *try/catch* @@ -449,4 +528,4 @@ In order to create a pull request # Resources * [Inspiration](https://quasimal.com/posts/2012-05-21-funsh.html) -* [Functional Programming in Bash](https://medium.com/@joydeepubuntu/functional-programming-in-bash-145b6db336b7) \ No newline at end of file +* [Functional Programming in Bash](https://medium.com/@joydeepubuntu/functional-programming-in-bash-145b6db336b7) From 142e1bade4bb5f0dd8edfce0852ba6add1af258d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=82awomir=20=C5=9Aled=C5=BA?= Date: Mon, 7 Oct 2019 10:27:02 +0200 Subject: [PATCH 24/26] Simplifying release procedure --- README.md | 4 ++-- release.sh | 47 ----------------------------------------------- 2 files changed, 2 insertions(+), 49 deletions(-) delete mode 100755 release.sh diff --git a/README.md b/README.md index 8d6aa66..30e5395 100644 --- a/README.md +++ b/README.md @@ -522,8 +522,8 @@ cd test Feel free to ask questions in chat, open issues, or contribute by creating pull requests. In order to create a pull request -* checkout develop branch -* introduce your changes +* checkout master branch +* introduce your changes & bump version * submit pull request # Resources diff --git a/release.sh b/release.sh deleted file mode 100755 index 219af38..0000000 --- a/release.sh +++ /dev/null @@ -1,47 +0,0 @@ -#!/bin/bash - -version=$(cat .version) - -release_version=${version%%-SNAPSHOT} -new_version=$(echo $release_version+0.1 | bc) - -[[ $? -ne 0 ]] && echo 'Error exiting.' && exit 1 - -snapshot_version=${new_version}-SNAPSHOT - -cat < ./.version -git add ./.version -git commit -m "[release] prepare release v$release_version" -git tag v$release_version -echo $snapshot_version > ./.version -git add ./.version -git commit -m "[release] prepare for next development iteration" - -echo merge the version back into develop -git checkout develop -git merge --no-ff -m "[release] merge release/$release_version into develop" release/$release_version - -git checkout master -echo merge the version back into master but use the tagged version instead of the release/$releaseVersion HEAD -git merge --no-ff -m "[release] merge previous version into master to avoid the increased version number" release/$release_version~1 - -echo get back on the develop branch -git checkout develop -echo finally push everything -git push origin develop master -git push --tags -echo removing the release branch -git branch -D release/$release_version From 46722c9b5d16efdbdc50a1e06ba9db5fd8d75649 Mon Sep 17 00:00:00 2001 From: Brandon Rozek Date: Mon, 14 Dec 2020 10:08:36 -0500 Subject: [PATCH 25/26] Simplified, shellchecked, and name deconflicted for personal use --- .version | 2 +- LICENSE | 1 + README.md | 314 ++-------------- src/fun.sh | 805 +++++++++++++++++++++++----------------- test/append_test.sh | 6 +- test/catch_test.sh | 19 - test/drop_test.sh | 10 +- test/head_test.sh | 8 +- test/last_test.sh | 8 +- test/map_test.sh | 21 +- test/maybe_test.sh | 29 -- test/predicates_test.sh | 10 +- test/prepend_test.sh | 6 +- test/tail_test.sh | 6 +- test/take_test.sh | 10 +- test/try_test.sh | 11 - test/tup_test.sh | 48 +-- 17 files changed, 540 insertions(+), 774 deletions(-) mode change 100755 => 100644 src/fun.sh delete mode 100755 test/catch_test.sh delete mode 100755 test/maybe_test.sh delete mode 100755 test/try_test.sh diff --git a/.version b/.version index 6b4950e..f9fe6b4 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -2.4 +2.5b diff --git a/LICENSE b/LICENSE index 4779b34..cf8e84f 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,6 @@ MIT License +Copyright (c) 2020 Brandon Rozek Copyright (c) 2019 Sławomir Śledź Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/README.md b/README.md index 30e5395..15673cd 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,15 @@ # Introduction -[Introduction to fun.sh library](http://ssledz.github.io/presentations/bash-fun.html#/) +This is a fork of [ssledz's fun.sh library](https://github.com/ssledz/bash-fun). +This is mainly for my own personal use cases. So I would recommend using ssledz's version instead. +I mainly worked towards getting this library to mostly pass shellcheck, removed some functionality, +and name deconflicted some of the functions with what I had in my system. # Quick start ```bash #!/bin/bash -. <(test -e fun.sh || curl -Ls https://raw.githubusercontent.com/ssledz/bash-fun/master/src/fun.sh > fun.sh; cat fun.sh) +. <(test -e fun.sh || curl -Ls https://raw.githubusercontent.com/brandon-rozek/bash-fun/master/src/fun.sh > fun.sh; cat fun.sh) seq 1 4 | sum ``` @@ -14,17 +17,17 @@ seq 1 4 | sum # Functions overview ||||||| |------|------|------|------|------|------| -|**append**|**buff**|**call**|**catch**|**curry**|**div**| -|**drop**|**dropw**|**factorial**|**filter**|**foldl**|**foldr**| +|**list_append**|**divide**|**take_while**| +|**list_drop**|**drop_while**|**factorial**|**filter**|**foldl**| |**isint**|**isempty**|**isfile**|**isnonzerofile**|**isreadable**|**iswritable**| -|**isdir**|**join**|**lambda**|**last**|**lhead**|**list**| -|**ltail**|**lzip**|**map**|**maybe**|**maybemap**|**maybevalue**| -|**mod**|**mul**|**not**|**ntup**|**ntupl**|**ntupr**| -|**ntupx**|**peek**|**plus**|**prepend**|**product**|**ret**| -|**res**|**revers**|**revers_str**|**scanl**|**splitc**|**strip**| -|**stripl**|**stripr**|**sub**|**sum**|**take**|**try**| -|**tup**|**tupl**|**tupr**|**tupx**|**unlist**|**λ**| -|**with_trampoline**| +|**isdir**|**list_join**|**lambda**|**list_last**|**list_head**|**list**| +|**list_tail**|**list_zip**|**list_map**| +|**mod**|**multiply**|**not**| +|**add**|**list_prepend**|**product**|**ret**| +|**revers**|**revers_str**|**scanl**|**splitc**|**strip**| +|**stripl**|**stripr**|**subtract**|**sum**|**take**| +|**tup**|**unlist**|**λ**| + ## *list/unlist* @@ -38,25 +41,25 @@ $ list 1 2 3 4 5 | unlist 1 2 3 4 5 ``` -## *take/drop/ltail/lhead/last* +## *list_take/list_drop/list_tail/list_head/list_last* ```bash -$ list 1 2 3 4 | drop 2 +$ list 1 2 3 4 | list_drop 2 3 4 -$ list 1 2 3 4 5 | lhead +$ list 1 2 3 4 5 | list_head 1 -$ list 1 2 3 4 | ltail +$ list 1 2 3 4 | list_tail 2 3 4 -$ list 1 2 3 4 5 | last +$ list 1 2 3 4 5 | list_last 5 -$ list 1 2 3 4 5 | take 2 +$ list 1 2 3 4 5 | list_take 2 1 2 ``` @@ -64,61 +67,36 @@ $ list 1 2 3 4 5 | take 2 ## *join* ```bash -$ list 1 2 3 4 5 | join , +$ list 1 2 3 4 5 | list_join , 1,2,3,4,5 - -$ list 1 2 3 4 5 | join , [ ] -[1,2,3,4,5] ``` ## *map* ```bash -$ seq 1 5 | map λ a . 'echo $((a + 5))' +$ seq 1 5 | list_map λ a . 'echo $((a + 5))' 6 7 8 9 10 -$ list a b s d e | map λ a . 'echo $a$(echo $a | tr a-z A-Z)' +$ list a b s d e | list_map λ a . 'echo $a$(echo $a | tr a-z A-Z)' aA bB sS dD eE -$ list 1 2 3 | map echo +$ list 1 2 3 | list_map tee 1 2 3 - -$ list 1 2 3 | map 'echo $ is a number' -1 is a number -2 is a number -3 is a number - -$ list 1 2 3 4 | map 'echo \($,$\) is a point' -(1,1) is a point -(2,2) is a point -(3,3) is a point -(4,4) is a point ``` - -## *flat map* - -```bash -$ seq 2 3 | map λ a . 'seq 1 $a' | join , [ ] -[1,2,1,2,3] - -$ list a b c | map λ a . 'echo $a; echo $a | tr a-z A-z' | join , [ ] -[a,A,b,B,c,C] -``` - ## *filter* ```bash -$ seq 1 10 | filter λ a . '[[ $(mod $a 2) -eq 0 ]] && ret true || ret false' +$ seq 1 10 | filter even 2 4 6 @@ -131,10 +109,6 @@ $ seq 1 10 | filter λ a . '[[ $(mod $a 2) -eq 0 ]] && ret true || ret false' ```bash $ list a b c d | foldl λ acc el . 'echo -n $acc-$el' a-b-c-d - -$ list '' a b c d | foldr λ acc el .\ - 'if [[ ! -z $acc ]]; then echo -n $acc-$el; else echo -n $el; fi' -d-c-b-a ``` ```bash @@ -143,11 +117,8 @@ $ seq 1 4 | foldl λ acc el . 'echo $(($acc + $el))' ``` ```bash -$ seq 1 4 | foldl λ acc el . 'echo $(mul $(($acc + 1)) $el)' +$ seq 1 4 | foldl λ acc el . 'echo $(multiply $(($acc + 1)) $el)' 64 # 1 + (1 + 1) * 2 + (4 + 1) * 3 + (15 + 1) * 4 = 64 - -$ seq 1 4 | foldr λ acc el . 'echo $(mul $(($acc + 1)) $el)' -56 # 1 + (1 + 1) * 4 + (8 + 1) * 3 + (27 + 1) * 2 = 56 ``` ## *tup/tupx/tupl/tupr* @@ -160,19 +131,16 @@ $ tup 'foo bar' 1 'one' 2 (foo bar,1,one,2) $ tup , 1 3 -(u002c,1,3) +(,,1,3) ``` ```bash -$ tupl $(tup a 1) +$ echo tup a 1 | tupl a -$ tupr $(tup a 1) +$ echo tup a 1 | tupr 1 -$ tup , 1 3 | tupl -, - $ tup 'foo bar' 1 'one' 2 | tupl foo bar @@ -180,63 +148,10 @@ $ tup 'foo bar' 1 'one' 2 | tupr 2 ``` -```bash -$ tup 'foo bar' 1 'one' 2 | tupx 2 -1 - -$ tup 'foo bar' 1 'one' 2 | tupx 1,3 -foo bar -one - -$ tup 'foo bar' 1 'one' 2 | tupx 2-4 -1 -one -2 -``` - -## *ntup/ntupx/ntupl/ntupr* +## *list_zip* ```bash -$ ntup tuples that $(ntup safely nest) -(dHVwbGVzCg==,dGhhdAo=,KGMyRm1aV3g1Q2c9PSxibVZ6ZEFvPSkK) - -echo '(dHVwbGVzCg==,dGhhdAo=,KGMyRm1aV3g1Q2c9PSxibVZ6ZEFvPSkK)' | ntupx 3 | ntupr -nest - -$ ntup 'foo,bar' 1 one 1 -(Zm9vLGJhcgo=,MQo=,b25lCg==,MQo=) - -$ echo '(Zm9vLGJhcgo=,MQo=,b25lCg==,MQo=)' | ntupx 1 -foo,bar -``` - -```bash -$ ntupl $(ntup 'foo bar' 1 one 2) -foo bar - -$ ntupr $(ntup 'foo bar' 1 one 2) -2 -``` - -## *buff* - -```bash -$ seq 1 10 | buff λ a b . 'echo $(($a + $b))' -3 -7 -11 -15 -19 - -$ seq 1 10 | buff λ a b c d e . 'echo $(($a + $b + $c + $d + $e))' -15 -40 -``` - -## *lzip* - -```bash -$ list a b c d e f | lzip $(seq 1 10) +$ list a b c d e f | list_zip $(seq 1 10) (a,1) (b,2) (c,3) @@ -246,86 +161,10 @@ $ list a b c d e f | lzip $(seq 1 10) ``` ```bash -$ list a b c d e f | lzip $(seq 1 10) | last | tupr +$ list a b c d e f | list_zip $(seq 1 10) | list_last | tupr 6 ``` -## *curry* - -```bash -add2() { - echo $(($1 + $2)) -} -``` - -```bash -$ curry inc add2 1 -``` - -```bash -$ inc 2 -3 - -$ seq 1 3 | map λ a . 'inc $a' -2 -3 -4 -``` - -## *peek* - -```bash -$ list 1 2 3 \ - | peek lambda a . echo 'dbg a : $a' \ - | map lambda a . 'mul $a 2' \ - | peek lambda a . echo 'dbg b : $a' \ - | sum - -dbg a : 1 -dbg a : 2 -dbg a : 3 -dbg b : 2 -dbg b : 4 -dbg b : 6 -12 -``` - -```bash -$ a=$(seq 1 4 | peek lambda a . echo 'dbg: $a' | sum) - -dbg: 1 -dbg: 2 -dbg: 3 -dbg: 4 - -$ echo $a - -10 -``` - -## *maybe/maybemap/maybevalue* - -```bash -$ list Hello | maybe -(Just,Hello) - -$ list " " | maybe -(Nothing) - -$ list Hello | maybe | maybemap λ a . 'tr oH Oh <<<$a' -(Just,hellO) - -$ list " " | maybe | maybemap λ a . 'tr oH Oh <<<$a' -(Nothing) - -$ echo bash-fun rocks | maybe | maybevalue DEFAULT -bash-fun rocks - -$ echo | maybe | maybevalue DEFAULT -DEFAULT - -``` - ## *not/isint/isempty* ```bash @@ -338,7 +177,7 @@ false $ not true false -$ not isint 777 +$ not "isint 777" false $ list 1 2 "" c d 6 | filter λ a . 'isint $a' @@ -346,7 +185,7 @@ $ list 1 2 "" c d 6 | filter λ a . 'isint $a' 2 6 -$ list 1 2 "" c d 6 | filter λ a . 'not isempty $a' +$ list 1 2 "" c d 6 | filter λ a . 'not "isempty $a"' 1 2 c @@ -393,35 +232,6 @@ $ list $files | filter λ a . 'not isfile $a' /tmp /no_such_file ``` - -## *try/catch* - -```bash -$ echo 'expr 2 / 0' | try λ _ . 'echo 0' -0 - -$ echo 'expr 2 / 0' | try λ status . 'echo $status' -2 - -$ echo 'expr 2 / 2' | try λ _ . 'echo 0' -1 -``` - -```bash -try λ _ . 'echo some errors during pull; exit 1' < <(echo git pull) -``` - -```bash -$ echo 'expr 2 / 0' \ - | LANG=en catch λ cmd status val . 'echo cmd=$cmd,status=$status,val=$val' -cmd=expr 2 / 0,status=2,val=(expr:,division,by,zero) -``` - -```bash -$ echo 'expr 2 / 2' | catch λ _ _ val . 'tupl $val' -1 -``` - ## *scanl* ```bash @@ -434,58 +244,10 @@ $ seq 1 5 | scanl lambda acc el . 'echo $(($acc + $el))' ``` ```bash -$ seq 1 5 | scanl lambda a b . 'echo $(($a + $b))' | last +$ seq 1 5 | scanl lambda a b . 'echo $(($a + $b))' | list_last 15 ``` -## *with_trampoline/res/call* - -```bash -factorial() { - fact_iter() { - local product=$1 - local counter=$2 - local max_count=$3 - if [[ $counter -gt $max_count ]]; then - res $product - else - call fact_iter $(echo $counter\*$product | bc) $(($counter + 1)) $max_count - fi - } - - with_trampoline fact_iter 1 1 $1 -} -``` - -```bash -$ time factorial 30 | fold -w 70 -265252859812191058636308480000000 - -real 0m1.854s -user 0m0.072s -sys 0m0.368s -``` - -```bash -time factorial 60 | fold -w 70 -8320987112741390144276341183223364380754172606361245952449277696409600 -000000000000 - -real 0m3.635s -user 0m0.148s -sys 0m0.692s -``` - -```bash -$ time factorial 90 | fold -w 70 -1485715964481761497309522733620825737885569961284688766942216863704985 -393094065876545992131370884059645617234469978112000000000000000000000 - -real 0m4.371s -user 0m0.108s -sys 0m0.436s -``` - # Examples ```bash @@ -498,7 +260,7 @@ processNames() { list $@ \ | filter λ name . '[[ ${#name} -gt 1 ]] && ret true || ret false' \ - | map λ name . 'uppercase $name' \ + | list_map λ name . 'uppercase $name' \ | foldl λ acc el . 'echo $acc,$el' } @@ -511,7 +273,7 @@ Adam,Monika,Slawek,Daniel,Bartek ``` # Running tests - +TODO: Need to change the tests here ```bash cd test ./test_runner diff --git a/src/fun.sh b/src/fun.sh old mode 100755 new mode 100644 index 08f3bc1..704ef28 --- a/src/fun.sh +++ b/src/fun.sh @@ -1,437 +1,548 @@ -#!/bin/bash - -drop() { - command tail -n +$(($1 + 1)) -} - -take() { - command head -n ${1} -} - -ltail() { - drop 1 -} - -lhead() { - take 1 -} - -last() { - command tail -n 1 -} +#!/bin/sh +############################################### +## List Functions +############################################### list() { - for i in "$@"; do - echo "$i" - done + for i in "$@"; do + echo "$i" + done } unlist() { - cat - | xargs + xargs } -append() { - cat - - list "$@" +# Drop the first n items of a list. +list_drop() { + command tail -n +$(($1 + 1)) } -prepend() { - list "$@" - cat - +# Take the first n items of a list. +list_take() { + command head -n "$1" } +# Take the 'tail' of a list. +# Otherwise known as dropping the first element. +list_tail() { + list_drop 1 +} +# Take only the first element of the list. +list_head() { + list_take 1 +} + +# Take the last element of the list. +list_last() { + command tail -n 1 +} + +# Add the contents of standard input +# to the end of the list. +list_append() { + cat - + list "$@" +} + +# Add the contents of standard input +# to the beginning of the list. +list_prepend() { + list "$@" + cat - +} + +############################################### +## Lambdas and Lists +############################################### +# Defines an anonymous function. lambda() { + # shellcheck disable=2039 + local expression + lam() { + # shellcheck disable=2039 + local arg + while [ $# -gt 0 ]; do + arg="$1" + shift + if [ "$arg" = '.' ]; then + echo "$@" + return + else + echo "read $arg;" + fi + done + } - lam() { - local arg - while [[ $# -gt 0 ]]; do - arg="$1" - shift - if [[ $arg = '.' ]]; then - echo "$@" - return - else - echo "read $arg;" - fi - done - } - - eval $(lam "$@") - + expression=$(lam "$@") + eval "$expression" } +# Same as lambda. +# shellcheck disable=2039 λ() { - lambda "$@" + lambda "$@" } -map() { - if [[ $1 != "λ" ]] && [[ $1 != "lambda" ]]; then +# Print the number of arguments a lambda takes. +# shellcheck disable=2039 +λ_num_args() { + # Calculates the number of arguments a lambda takes + minus "$#" 3 +} - local has_dollar=$(list $@ | grep '\$' | wc -l) - - if [[ $has_dollar -ne 0 ]]; then - args=$(echo $@ | sed -e 's/\$/\$a/g') - map λ a . $args - else - map λ a . "$@"' $a' - fi - else +# Perform an operation to each +# element(s) of a list provided +# through standard input. +list_map() { + # shellcheck disable=2039 local x - while read x; do - echo "$x" | "$@" - done - fi -} - -foldl() { - local f="$@" - local acc - read acc - while read elem; do - acc="$({ echo $acc; echo $elem; } | $f )" - done - echo "$acc" -} - -foldr() { - local f="$@" - local acc - local zero - read zero - foldrr() { - local elem - - if read elem; then - acc=$(foldrr) -# [[ -z $acc ]] && echo $elem && return - else - echo $zero && return + # shellcheck disable=2039 + local i + # shellcheck disable=2039 + local arguments + # shellcheck disable=2039 + local num_args + if [ "$1" = "λ" ] || [ "$1" = "lambda" ]; then + num_args=$(λ_num_args "$@") + while read -r x; do + arguments="$x" + i=2 + while [ $i -le "$num_args" ] ; do + read -r x + arguments="$arguments $x" + i=$(add $i 1) + done + # We want to word split arguments, so no quotes + eval "list $arguments" | "$@" + done + else # Do not know the arity, assume 1 + while read -r x; do + echo "$x" | "$@" + done fi - - acc="$({ echo $acc; echo $elem; } | $f )" - echo "$acc" - } - - foldrr } +# Perform a binary operation on a list +# where one element is the accumulation +# of the results so far. +# Ex: seq 3 | foldl lambda a b . 'minus $a $b' +# First is (1 - 2 = -1) then (-1 - 3 = -4). +foldl() { + # shellcheck disable=2039 + local acc + read -r acc + while read -r elem; do + acc=$({ echo "$acc"; echo "$elem"; } | "$@" ) + done + echo "$acc" +} + +# Constructs a list where each element +# is the foldl of the 0th-ith elements of +# the list. scanl() { - local f="$@" - local acc - read acc - echo $acc - while read elem; do - acc="$({ echo $acc; echo $elem; } | $f )" + # shellcheck disable=2039 + local acc + read -r acc echo "$acc" - done + while read -r elem; do + acc=$({ echo "$acc"; echo "$elem"; } | "$@" ) + echo "$acc" + done } -mul() { - ( set -f; echo $(($1 * $2)) ) +# Drops any elements of the list where the +# function performed on it evaluates to false. +filter() { + # shellcheck disable=2039 + local x + while read -r x; do + ret=$(echo "$x" | "$@") + if_then "$ret" "echo $x" + done } -plus() { - echo $(($1 + $2)) +# Keep taking elements until a certain condition +# is false. +take_while() { + # shellcheck disable=2039 + local x + # shellcheck disable=2039 + local condition + while read -r x; do + condition="$(echo "$x" | "$@")" + if_then_else "$condition" "echo $x" "break" + done } -sub() { - echo $(($1 - $2)) +# Keep dropping elements until a certain condition +# is false. +drop_while() { + # shellcheck disable=2039 + local x + while read -r x; do + condition="$(echo "$x" | "$@")" + if_then_else "$condition" 'do_nothing' 'break' + done + if_then "[ -n $x ]" "{ echo $x; cat -; }" } -div() { - echo $(($1 / $2)) + +############################################### +## Arithmetic Functions +############################################### +multiply() { + # shellcheck disable=2039 + local a + # shellcheck disable=2039 + local b + a=$1 + if [ $# -lt 2 ] ; then + read -r b + else + b=$2 + fi + isint "$a" > /dev/null && \ + isint "$b" > /dev/null && \ + echo $((a * b)) +} + +add() { + # shellcheck disable=2039 + local a + # shellcheck disable=2039 + local b + a=$1 + if [ $# -lt 2 ] ; then + read -r b + else + b=$2 + fi + isint "$a" > /dev/null && \ + isint "$b" > /dev/null && \ + echo $((a + b)) +} + +minus() { + # shellcheck disable=2039 + local a + # shellcheck disable=2039 + local b + a=$1 + if [ $# -lt 2 ] ; then + b=$1 + read -r a + else + b=$2 + fi + isint "$a" > /dev/null && \ + isint "$b" > /dev/null && \ + echo $((a - b)) +} + +divide() { + # shellcheck disable=2039 + local a + # shellcheck disable=2039 + local b + a=$1 + if [ $# -lt 2 ] ; then + b=$1 + read -r a + else + b=$2 + fi + isint "$a" > /dev/null && \ + isint "$b" > /dev/null && \ + echo $((a / b)) } mod() { - echo $(($1 % $2)) + # shellcheck disable=2039 + local a + # shellcheck disable=2039 + local b + a=$1 + if [ $# -lt 2 ] ; then + b=$1 + read -r a + else + b=$2 + fi + isint "$a" > /dev/null && \ + isint "$b" > /dev/null && \ + echo $((a % b)) } +even() { + # shellcheck disable=2039 + local n + # shellcheck disable=2039 + local result + # shellcheck disable=2039 + local result_code + if [ $# -lt 1 ] ; then + read -r n + else + n=$1 + fi + result=$(mod "$n" 2) + result_code=$? + if [ $result_code -ne 0 ] ; then + ret false + else + result_to_bool "[ $result = 0 ]" + fi +} + +odd() { + not even +} + +less_than() { + # shellcheck disable=2039 + local n + read -r n + if isint "$n" > /dev/null && \ + [ "$n" -lt "$1" ] ; then + ret true + else + ret false + fi +} sum() { - foldl lambda a b . 'echo $(($a + $b))' + foldl lambda a b . "add \$a \$b" } product() { - foldl lambda a b . 'echo $(mul $a $b)' + foldl lambda a b . "multiply \$a \$b" } factorial() { - seq 1 $1 | product + seq 1 "$1" | product } +############################################### +## String Operations +############################################### +# Splits a string into a list where each element +# is one character. splitc() { - cat - | sed 's/./&\n/g' + sed 's/./\n&/g' | list_tail } -join() { - local delim=$1 - local pref=$2 - local suff=$3 - echo $pref$(cat - | foldl lambda a b . 'echo $a$delim$b')$suff +# Takes a list and creates a string where +# each element is seperated by a delimiter. +list_join() { + # shellcheck disable=2039 + local delim + delim=$1 + foldl lambda a b . "echo \$a$delim\$b" } +# Split a string into a list +# by a specified delimeter +str_split() { + sed "s/$1/\n/g" +} + +# Reverses a list. revers() { - foldl lambda a b . 'append $b $a' + # shellcheck disable=2039 + local result + # shellcheck disable=2039 + local n + while read -r n; do + result="$n\n$result" + done + echo "$result" } -revers_str() { - cat - | splitc | revers | join +# Removes multiple occurences of +# a single character from the beginning +# of the list. +lstrip() { + # shellcheck disable=2039 + local c + if [ $# -eq 0 ] ; then + c=" " + else + c="$1" + fi + sed "s/^$c*//g" } -catch() { - local f="$@" - local cmd=$(cat -) - local val=$(2>&1 eval "$cmd"; echo $?) - local cnt=$(list $val | wc -l) - local status=$(list $val | last) - $f < <(list "$cmd" $status $(list $val | take $((cnt - 1)) | unlist | tup)) +# Removes multiple occurences of +# a single character from the end +# of the list. +rstrip() { + # shellcheck disable=2039 + local c + if [ $# -eq 0 ] ; then + c=" " + else + c="$1" + fi + sed "s/$c*$//g" } -try() { - local f="$@" - catch lambda cmd status val . '[[ $status -eq 0 ]] && tupx 1- $val | unlist || { '"$f"' < <(list $status); }' +# Removes multiple occurences of +# a single character from the beginning +# and end of the list. +strip() { + lstrip "$@" | rstrip "$@" +} + +############################################### +## Tuple Functions +############################################### + +# Creates a tuple, which is a string with +# multiple elements seperated by a comma, +# and it begins with a ( and ends with a ). +tup() { + # shellcheck disable=2039 + local args + # shellcheck disable=2039 + local result + if [ $# -eq 0 ]; then + args=$(unlist) + eval "tup $args" + else + result=$(list "$@" | list_join ,) + echo "($result)" + fi +} + +# Takes a tuple and outputs it as a list +tup_to_list() { + local li + local f + local la + li=$(str_split ",") + + # Remove '(' from the first element + f=$(echo "$li" | list_head) + f=$(echo "$f" | sed 's/^(//') + + la=$(echo "$li" | list_last) + # If there is only one element in the list + # Remove ')' from the only element + if [ "$(echo "$la" | cut -c1)" = "(" ]; then + f=$(echo "$f" | sed "s/)$//") + echo "$f" + # If there is more than one element in the list + # Remove ')' from the last element + else + la=$(echo "$la" | sed "s/)$//") + # Remove the first and last element from li + li=$(echo "$li" | list_tail | sed '$d') + # Print the list + { echo "$f"; echo "$li"; echo "$la"; } + fi +} + +# Takes the first element of the tuple +tupl() { + tup_to_list | list_head +} + +# Takes the last element of the tuple +tupr() { + tup_to_list | list_last +} + + +# Takes each element from a list in standard +# input and matches it with a list provided +# as the argument to this function. +# The result is a list of 2-tuples. +list_zip() { + # shellcheck disable=2039 + local l + l=$(list "$@") + while read -r x; do + y=$(echo "$l" | list_take 1) + tup "$x" "$y" + l=$(echo "$l" | list_drop 1) + done +} + +############################################### +## Logic Based Functions +############################################### + +if_then() { + # shellcheck disable=2039 + local result + eval "$1" + result=$? + if [ $result -eq 0 ] ; then + eval "$2" + fi +} + +if_then_else() { + # shellcheck disable=2039 + local result + eval "$1" + result=$? + if [ $result -eq 0 ] ; then + eval "$2" + else + eval "$3" + fi +} + +result_to_bool() { + if_then_else "$1" 'ret true' 'ret false' +} + +not() { + if_then_else "$1 > /dev/null" "ret false" "ret true" } ret() { - echo $@ + echo "$@" + "$@" } -filter() { - local x - while read x; do - ret=$(echo "$x" | "$@") - $ret && echo $x - done -} - -pass() { - echo > /dev/null -} - -dropw() { - local x - while read x && $(echo "$x" | "$@"); do - pass - done - [[ ! -z $x ]] && { echo $x; cat -; } -} - -peek() { - local x - while read x; do - ([ $# -eq 0 ] && 1>&2 echo $x || 1>&2 "$@" < <(echo $x)) - echo $x - done -} - -stripl() { - local arg=$1 - cat - | map lambda l . 'ret ${l##'$arg'}' -} - -stripr() { - local arg=$1 - cat - | map lambda l . 'ret ${l%%'$arg'}' -} - -strip() { - local arg=$1 - cat - | stripl "$arg" | stripr "$arg" -} - -buff() { - local cnt=-1 - for x in $@; do - [[ $x = '.' ]] && break - cnt=$(plus $cnt 1) - done - local args='' - local i=$cnt - while read arg; do - [[ $i -eq 0 ]] && list $args | "$@" && i=$cnt && args='' - args="$args $arg" - i=$(sub $i 1) - done - [[ ! -z $args ]] && list $args | "$@" -} - -tup() { - if [[ $# -eq 0 ]]; then - local arg - read arg - tup $arg - else - list "$@" | map lambda x . 'echo ${x//,/u002c}' | join , '(' ')' - fi -} - -tupx() { - if [[ $# -eq 1 ]]; then - local arg - read arg - tupx "$1" "$arg" - else - local n=$1 - shift - echo "$@" | stripl '(' | stripr ')' | cut -d',' -f${n} | tr ',' '\n' | map lambda x . 'echo ${x//u002c/,}' - fi -} - -tupl() { - tupx 1 "$@" -} - -tupr() { - tupx 1- "$@" | last -} - -ntup() { - if [[ $# -eq 0 ]]; then - local arg - read arg - ntup $arg - else - list "$@" | map lambda x . 'echo "$x" | base64 --wrap=0 ; echo' | join , '(' ')' - fi -} - -ntupx() { - if [[ $# -eq 1 ]]; then - local arg - read arg - ntupx "$1" "$arg" - else - local n=$1 - shift - echo "$@" | stripl '(' | stripr ')' | cut -d',' -f${n} | tr , '\n' | map lambda x . 'echo "$x" | base64 -d' - fi -} - -ntupl() { - ntupx 1 "$@" -} - -ntupr() { - ntupx 1- "$@" | last -} - -lzip() { - local list=$* - cat - | while read x; do - y=$(list $list | take 1) - tup $x $y - list=$(list $list | drop 1) - done -} - -curry() { - exportfun=$1; shift - fun=$1; shift - params=$* - cmd=$"function $exportfun() { - more_params=\$*; - $fun $params \$more_params; - }" - eval $cmd -} - -with_trampoline() { - local f=$1; shift - local args=$@ - while [[ $f != 'None' ]]; do - ret=$($f $args) -# echo $ret - f=$(tupl $ret) - args=$(echo $ret | tupx 2- | tr ',' ' ') - done - echo $args -} - -res() { - local value=$1 - tup "None" $value -} - -call() { - local f=$1; shift - local args=$@ - tup $f $args -} - -maybe() { - if [[ $# -eq 0 ]]; then - local arg - read arg - maybe "$arg" - else - local x="$*" - local value=$(echo $x | strip) - if [[ ${#value} -eq 0 ]]; then - tup Nothing - else - tup Just "$value" - fi - fi -} - -maybemap() { - local x - read x - if [[ $(tupl $x) = "Nothing" ]]; then - echo $x - else - local y=$(tupr "$x") - local r=$(echo "$y" | map "$@") - maybe "$r" - fi -} - -maybevalue() { - local default="$*" - local x - read x - if [[ $(tupl $x) = "Nothing" ]]; then - echo "$default" - else - echo $(tupr $x) - fi +do_nothing() { + echo > /dev/null } -# commonly used predicates for filter -# e.g. list 1 a 2 b 3 c | filter lambda x . 'isint $x' - -# inverse another test, e.g. "not isint $x" -not() { - local r=$("$@" 2>/dev/null) - $r && ret false || ret true -} +############################################### +## Useful utility functions +############################################### isint() { - [ "$1" -eq "$1" ] 2>/dev/null && ret true || ret false + result_to_bool "echo \"$1\" | grep -Eq '^-?[0-9]+$'" } isempty() { - [ -z "$1" ] && ret true || ret false + result_to_bool "[ -z \"$1\" ]" } isfile() { - [ -f "$1" ] && ret true || ret false + result_to_bool "[ -f \"$1\" ]" } isnonzerofile() { - [ -s "$1" ] && ret true || ret false + result_to_bool "[ -s \"$1\" ]" } isreadable() { - [ -r "$1" ] && ret true || ret false + result_to_bool "[ -r \"$1\" ]" } iswritable() { - [ -w "$1" ] && ret true || ret false + result_to_bool "[ -w \"$1\" ]" } isdir() { - [ -d "$1" ] && ret true || ret false + result_to_bool "[ -d \"$1\" ]" } diff --git a/test/append_test.sh b/test/append_test.sh index e6ceb99..2ee28fc 100755 --- a/test/append_test.sh +++ b/test/append_test.sh @@ -1,15 +1,15 @@ #! /bin/bash testAppendToEmptyList() { - assertEquals 4 "$(list | append 4)" + assertEquals 4 "$(list | list_append 4)" } testAppendToOneElementList() { - assertEquals "1 4" "$(list 1 | append 4 | unlist)" + assertEquals "1 4" "$(list 1 | list_append 4 | unlist)" } testAppendToList() { - assertEquals "1 2 3 4 5 4" "$(list 1 2 3 4 5 | append 4 | unlist)" + assertEquals "1 2 3 4 5 4" "$(list 1 2 3 4 5 | list_append 4 | unlist)" } . ./shunit2-init.sh \ No newline at end of file diff --git a/test/catch_test.sh b/test/catch_test.sh deleted file mode 100755 index b3f0917..0000000 --- a/test/catch_test.sh +++ /dev/null @@ -1,19 +0,0 @@ -#! /bin/bash - -testCatchIfSuccess() { - assertEquals 1 "$(echo 'expr 2 / 2' | catch lambda cmd status val . '[[ $status -eq 0 ]] && tupl $val || echo 0')" -} - -testCatchIfError() { - assertEquals 0 $(echo 'expr 2 / 0' | catch lambda cmd status val . '[[ $status -eq 0 ]] && tupl $val || echo 0') - assertEquals 'cmd=expr 2 / 0,status=2,val=(expr:,division,by,zero)' "$(echo 'expr 2 / 0' | echo 'expr 2 / 0' | LANG=en catch lambda cmd status val . 'echo cmd=$cmd,status=$status,val=$val')" -} - -testCatchEdgeCases() { - assertEquals 1 "$(echo 'expr 2 / 2' | catch lambda _ _ val . 'tupl $val')" - assertEquals 'expr 2 / 2' "$(echo 'expr 2 / 2' | catch lambda cmd . 'ret $cmd')" - assertEquals 'expr 2 / 2,0' "$(echo 'expr 2 / 2' | catch lambda cmd status . 'ret $cmd,$status')" - assertEquals 'expr 2 / 0,2' "$(echo 'expr 2 / 0' | catch lambda cmd status . 'ret $cmd,$status')" -} - -. ./shunit2-init.sh \ No newline at end of file diff --git a/test/drop_test.sh b/test/drop_test.sh index 5aef523..7195d48 100755 --- a/test/drop_test.sh +++ b/test/drop_test.sh @@ -1,23 +1,23 @@ #! /bin/bash testDrop9From10() { - assertEquals 10 $(list {1..10} | drop 9) + assertEquals 10 $(list {1..10} | list_drop 9) } testDrop8From10() { - assertEquals "9 10" "$(list {1..10} | drop 8 | unlist)" + assertEquals "9 10" "$(list {1..10} | list_drop 8 | unlist)" } testDropAll() { - assertEquals "" "$(list {1..10} | drop 10)" + assertEquals "" "$(list {1..10} | list_drop 10)" } testDropMoreThanAvailable() { - assertEquals "" "$(list {1..10} | drop 15)" + assertEquals "" "$(list {1..10} | list_drop 15)" } testDropZero() { - assertEquals "1 2 3 4 5 6 7 8 9 10" "$(list {1..10} | drop 0 | unlist)" + assertEquals "1 2 3 4 5 6 7 8 9 10" "$(list {1..10} | list_drop 0 | unlist)" } . ./shunit2-init.sh \ No newline at end of file diff --git a/test/head_test.sh b/test/head_test.sh index 90ea201..0293890 100755 --- a/test/head_test.sh +++ b/test/head_test.sh @@ -1,16 +1,16 @@ #! /bin/bash testLHeadFromList() { - assertEquals 1 $(list {1..10} | lhead) - assertEquals 5 $(list 5 6 7 | lhead) + assertEquals 1 $(list {1..10} | list_head) + assertEquals 5 $(list 5 6 7 | list_head) } testLHeadFromOneElementList() { - assertEquals 1 $(list 1 | lhead) + assertEquals 1 $(list 1 | list_head) } testLHeadFromEmptyList() { - assertEquals "" "$(list | lhead)" + assertEquals "" "$(list | list_head)" } . ./shunit2-init.sh diff --git a/test/last_test.sh b/test/last_test.sh index e76a694..e10ec36 100755 --- a/test/last_test.sh +++ b/test/last_test.sh @@ -1,16 +1,16 @@ #! /bin/bash testLastFromList() { - assertEquals 10 $(list {1..10} | last) - assertEquals 7 $(list 5 6 7 | last) + assertEquals 10 $(list {1..10} | list_last) + assertEquals 7 $(list 5 6 7 | list_last) } testLastFromOneElementList() { - assertEquals 1 $(list 1 | last) + assertEquals 1 $(list 1 | list_last) } testLastFromEmptyList() { - assertEquals "" "$(list | last)" + assertEquals "" "$(list | list_last)" } . ./shunit2-init.sh \ No newline at end of file diff --git a/test/map_test.sh b/test/map_test.sh index 5371349..383a8a8 100755 --- a/test/map_test.sh +++ b/test/map_test.sh @@ -1,38 +1,33 @@ #!/bin/bash testMapEmptyList() { - assertEquals "" "$(list | map lambda x . 'echo $(($x + 1))')" + assertEquals "" "$(list | list_map lambda x . 'echo $(($x + 1))')" } testMapEmptyList_ifNoArgumentsInLambda() { - assertEquals "" "$(list | map lambda . 'echo 3')" + assertEquals "" "$(list | list_map lambda . 'echo 3')" } testMapOneElementList() { - assertEquals "3" "$(list 2 | map lambda x . 'echo $(($x + 1))')" + assertEquals "3" "$(list 2 | list_map lambda x . 'echo $(($x + 1))')" } testMapList() { - assertEquals "2 3 4 5 6" "$(list {1..5} | map lambda x . 'echo $(($x + 1))' | unlist)" + assertEquals "2 3 4 5 6" "$(list {1..5} | list_map lambda x . 'echo $(($x + 1))' | unlist)" } testMapList_ifNoArgumentsInLambda() { - assertEquals "9 9 9 9 9" "$(list {1..5} | map lambda . 'echo 9' | unlist)" + assertEquals "9 9 9 9 9" "$(list {1..5} | list_map lambda . 'echo 9' | unlist)" } testMapList_ifManyArgumentsInLambda() { - list {1..5} | map lambda x y . 'echo $(($x + $y))' 2> /dev/null \ + list {1..5} | list_map lambda x y . 'echo $(($x + $y))' 2> /dev/null \ && fail "There should be syntax error, because map is an one argument operation" } testFlatMap() { - assertEquals "1 2 3 2 3 3" "$(list {1..3} | map lambda x . 'seq $x 3' | unlist)" - assertEquals "d e h l l l o o r w" "$(list hello world | map lambda x . 'command fold -w 1 <<< $x' | sort | unlist)" -} - -testMapNoLambdaSyntax() { - assertEquals "1 2 3" "$(list 1 2 3 | map echo | unlist)" - assertEquals "1 is a number 2 is a number 3 is a number" "$(list 1 2 3 | map 'echo $ is a number' | unlist)" + assertEquals "1 2 3 2 3 3" "$(list {1..3} | list_map lambda x . 'seq $x 3' | unlist)" + assertEquals "d e h l l l o o r w" "$(list hello world | list_map lambda x . 'command fold -w 1 <<< $x' | sort | unlist)" } . ./shunit2-init.sh \ No newline at end of file diff --git a/test/maybe_test.sh b/test/maybe_test.sh deleted file mode 100755 index 1003179..0000000 --- a/test/maybe_test.sh +++ /dev/null @@ -1,29 +0,0 @@ -#! /bin/bash - -testMaybe() { - assertEquals '(Just,1)' "$(maybe 1)" - assertEquals '(Just,1)' "$(echo 1 | maybe)" - assertEquals '(Nothing)' "$(maybe '')" - assertEquals '(Nothing)' "$(maybe ' ')" - assertEquals '(Nothing)' "$(maybe ' ' ' ' ' ')" - assertEquals '(Nothing)' "$(echo | maybe)" - assertEquals '(Just,1 2 3)' "$(maybe 1 2 3)" - assertEquals '(Just,1 2 3)' "$(echo 1 2 3 | maybe)" -} - -testMaybemap() { - assertEquals '(Just,3)' "$(echo 1 | maybe | maybemap lambda a . 'echo $(( a + 1 ))' | maybemap lambda a . 'echo $(( a + 1 ))')" - assertEquals '(Nothing)' "$(echo | maybe | maybemap lambda a . 'echo $(( a + 1 ))' | maybemap lambda a . 'echo $(( a + 1 ))')" - - assertEquals '(Nothing)' "$(echo 1 | maybe | maybemap lambda a . 'echo $(( a + 1 ))' | maybemap lambda a . 'echo')" - assertEquals '(Nothing)' "$(echo 1 | maybe | maybemap lambda a . 'echo $(( a + 1 ))' | maybemap lambda a . 'echo' | maybemap lambda a . 'echo $(( a + 1 ))')" -} - -testMaybevalue() { - assertEquals 3 "$(echo 1 | maybe | maybemap lambda a . 'echo $(( a + 1 ))' | maybemap lambda a . 'echo $(( a + 1 ))' | maybevalue 0)" - assertEquals 0 "$(echo | maybe | maybemap lambda a . 'echo $(( a + 1 ))' | maybemap lambda a . 'echo $(( a + 1 ))' | maybevalue 0)" - assertEquals 'a b c' "$(echo | maybe | maybemap lambda a . 'echo $(( a + 1 ))' | maybemap lambda a . 'echo $(( a + 1 ))' | maybevalue a b c)" -} - - -. ./shunit2-init.sh diff --git a/test/predicates_test.sh b/test/predicates_test.sh index 79bff20..5e96337 100755 --- a/test/predicates_test.sh +++ b/test/predicates_test.sh @@ -8,16 +8,16 @@ testIsint() { assertEquals '1 2 3 4 5' "$(list 1 a 2 b 3 c 4 d 5 e | filter lambda x . 'isint $x' | unlist )" assertEquals '1 2' "$(list 1 a 2 b 3 c 4 d 5 e | filter lambda x . '($(isint $x) && [[ $x -le 2 ]] && ret true) || ret false ' | unlist )" - assertEquals 'false' $(not isint 1) - assertEquals 'true' $(not isint a) + assertEquals 'false' $(not "isint 1") + assertEquals 'true' $(not "isint a") } testIsempty() { assertEquals 'true' $(isempty "") assertEquals 'false' $(isempty a) - assertEquals 'true' $(not isempty a) - assertEquals 'false' $(not isempty "") + assertEquals 'true' $(not "isempty a") + assertEquals 'false' $(not "isempty \"\"") } testIsfile() { @@ -26,7 +26,7 @@ testIsfile() { assertEquals 'true' $(isfile $f) assertEquals 'false' $(isfile $f.xxx) assertEquals 'false' $(isfile "") - assertEquals 'true' $(not isfile $f.xxx) + assertEquals 'true' $(not "isfile $f.xxx") assertEquals 'false' $(isnonzerofile $f) echo hello world >$f diff --git a/test/prepend_test.sh b/test/prepend_test.sh index 8e9e3a0..491a56e 100755 --- a/test/prepend_test.sh +++ b/test/prepend_test.sh @@ -1,15 +1,15 @@ #! /bin/bash testPrependToEmptyList() { - assertEquals 4 "$(list | prepend 4)" + assertEquals 4 "$(list | list_prepend 4)" } testPrependToOneElementList() { - assertEquals "4 1" "$(list 1 | prepend 4 | unlist)" + assertEquals "4 1" "$(list 1 | list_prepend 4 | unlist)" } testPrependToList() { - assertEquals "4 1 2 3 4 5" "$(list 1 2 3 4 5 | prepend 4 | unlist)" + assertEquals "4 1 2 3 4 5" "$(list 1 2 3 4 5 | list_prepend 4 | unlist)" } . ./shunit2-init.sh \ No newline at end of file diff --git a/test/tail_test.sh b/test/tail_test.sh index b2b19f5..f6c11e6 100755 --- a/test/tail_test.sh +++ b/test/tail_test.sh @@ -1,15 +1,15 @@ #! /bin/bash testLTailFrom10() { - assertEquals "2 3 4 5 6 7 8 9 10" "$(list {1..10} | ltail | unlist)" + assertEquals "2 3 4 5 6 7 8 9 10" "$(list {1..10} | list_tail | unlist)" } testLTailFromOneElementList() { - assertEquals "" "$(list 1 | ltail)" + assertEquals "" "$(list 1 | list_tail)" } testLTailFromEmptyList() { - assertEquals "" "$(list | ltail)" + assertEquals "" "$(list | list_tail)" } . ./shunit2-init.sh diff --git a/test/take_test.sh b/test/take_test.sh index efd48ef..5b0cb49 100755 --- a/test/take_test.sh +++ b/test/take_test.sh @@ -1,23 +1,23 @@ #! /bin/bash testTake9From10() { - assertEquals "1 2 3 4 5 6 7 8 9" "$(list {1..10} | take 9 | unlist)" + assertEquals "1 2 3 4 5 6 7 8 9" "$(list {1..10} | list_take 9 | unlist)" } testTake8From10() { - assertEquals "1 2 3 4 5 6 7 8" "$(list {1..10} | take 8 | unlist)" + assertEquals "1 2 3 4 5 6 7 8" "$(list {1..10} | list_take 8 | unlist)" } testTakeAll() { - assertEquals "1 2 3 4 5 6 7 8 9 10" "$(list {1..10} | take 10 | unlist)" + assertEquals "1 2 3 4 5 6 7 8 9 10" "$(list {1..10} | list_take 10 | unlist)" } testTakeMoreThanAvailable() { - assertEquals "1 2 3 4 5 6 7 8 9 10" "$(list {1..10} | take 15 | unlist)" + assertEquals "1 2 3 4 5 6 7 8 9 10" "$(list {1..10} | list_take 15 | unlist)" } testTakeZero() { - assertEquals "" "$(list {1..10} | take 0 | unlist)" + assertEquals "" "$(list {1..10} | list_take 0 | unlist)" } . ./shunit2-init.sh \ No newline at end of file diff --git a/test/try_test.sh b/test/try_test.sh deleted file mode 100755 index be1aeb3..0000000 --- a/test/try_test.sh +++ /dev/null @@ -1,11 +0,0 @@ -#! /bin/bash - -testTry() { - assertEquals 1 "$(echo 'expr 2 / 2' | try lambda _ . 'ret 0')" - assertEquals 0 "$(echo 'expr 2 / 0' | try lambda _ . 'ret 0')" - assertEquals 2 "$(echo 'expr 2 / 0' | try lambda status . 'ret $status')" - assertEquals 'already up to date' "$(echo 'echo already up to date' | try lambda _ . 'ret error')" - assertEquals 'error exit 1' "$(try λ _ . 'echo "error"; echo exit 1' < <(echo fgit pull) | unlist)" -} - -. ./shunit2-init.sh \ No newline at end of file diff --git a/test/tup_test.sh b/test/tup_test.sh index 25cd8bf..71f1b05 100755 --- a/test/tup_test.sh +++ b/test/tup_test.sh @@ -8,8 +8,8 @@ testTupIfOneElement() { assertEquals '(1)' $(tup 1) assertEquals '(")' $(tup '"') assertEquals "(')" $(tup "'") - assertEquals "(u002c)" $(tup ",") - assertEquals "(u002cu002c)" $(tup ",,") + assertEquals "(,)" $(tup ",") + assertEquals "(,,)" $(tup ",,") assertEquals "(()" $(tup "(") assertEquals "())" $(tup ")") } @@ -20,42 +20,10 @@ testTupHappyPath() { assertEquals '(a b,c d e,f)' "$(tup 'a b' 'c d e' 'f')" } -testTupxHappyPath() { - assertEquals '4' $(tup 4 5 1 4 | tupx 1) - assertEquals '5' $(tup 4 5 1 4 | tupx 2) - assertEquals '1' $(tup 4 5 1 4 | tupx 3) - assertEquals '4' $(tup 4 5 1 4 | tupx 4) - -} - -testTupxIfEmpty() { - assertEquals '' "$(tup '' | tupx 1)" - assertEquals '' "$(tup '' | tupx 5)" -} - testTupxIfZeroIndex() { assertEquals '' "$(tup 1 3 | tupx 0 2>/dev/null)" } -testTupxIfSpecialChars() { - assertEquals ',' "$(tup ',' | tupx 1)" - assertEquals ',,' "$(tup ',,' | tupx 1)" - assertEquals '(' "$(tup '(' | tupx 1)" - assertEquals ')' "$(tup ')' | tupx 1)" - assertEquals '()' "$(tup '()' | tupx 1)" - assertEquals '(' "$(tup '(' ')' | tupx 1)" - assertEquals '(' "$(tup '(' '(' | tupx 1)" - assertEquals ')' "$(tup ')' ')' | tupx 1)" - assertEquals ',' "$(tup 'u002c' | tupx 1)" - assertEquals ',,' "$(tup 'u002cu002c' | tupx 1)" -} - -testTupxRange() { - assertEquals '4 5' "$(tup 4 5 1 4 | tupx 1-2 | unlist)" - assertEquals '4 4' "$(tup 4 5 1 4 | tupx 1,4 | unlist)" - assertEquals '4 5 4' "$(tup 4 5 1 4 | tupx 1,2,4 | unlist)" -} - testTupl() { assertEquals '4' "$(tup 4 5 | tupl)" assertEquals '4' "$(tup 4 5 6 | tupl)" @@ -69,16 +37,4 @@ testTupr() { assertEquals '5' "$(tup 5 | tupr)" } -testNTup() { - assertEquals '(KFlRbz0sWWdvPSkK,Ywo=)' "$(ntup $(ntup a b) c)" - assertEquals '(YQo=,Ygo=)' "$(ntupl '(KFlRbz0sWWdvPSkK,Ywo=)')" - assertEquals 'a' "$(ntupl '(YQo=,Ygo=)')" - assertEquals 'b' "$(ntupr '(YQo=,Ygo=)')" - assertEquals 'c' "$(ntupr '(KFlRbz0sWWdvPSkK,Ywo=)')" - assertEquals 'a' "$(ntup $(ntup a b) c | ntupx 1 | ntupx 1)" - assertEquals 'b' "$(ntup $(ntup a b) c | ntupx 1 | ntupx 2)" - assertEquals 'c' "$(ntup $(ntup a b) c | ntupx 2)" - assertEquals 'a b' "$(ntup $(ntup a b) c | ntupx 1 | ntupx 1,2 | unlist)" -} - . ./shunit2-init.sh From 705798eeb09c726e75df52a345922d89d90ab808 Mon Sep 17 00:00:00 2001 From: Brandon Rozek Date: Mon, 14 Dec 2020 10:19:02 -0500 Subject: [PATCH 26/26] Added back revers_str and changed the example script --- examples/example.sh | 147 +++++++++----------------------------------- src/fun.sh | 5 ++ 2 files changed, 33 insertions(+), 119 deletions(-) diff --git a/examples/example.sh b/examples/example.sh index 9bcc29f..c30c455 100755 --- a/examples/example.sh +++ b/examples/example.sh @@ -4,26 +4,26 @@ source ../src/fun.sh seq 1 4 | sum seq 1 4 | product factorial 4 -seq 1 4 | scanl lambda a b . 'echo $(plus $a $b)' -echo map mul -seq 1 4 | map lambda a . 'echo $(mul $a 2)' -echo map sub -seq 1 4 | map lambda a . 'echo $(sub $a 2)' -echo map plsu -seq 1 4 | map lambda a . 'echo $(plus $a 2)' -echo map div -seq 1 4 | map lambda a . 'echo $(div $a 2)' -echo map mod -seq 1 4 | map lambda a . 'echo $(mod $a 2)' +seq 1 4 | scanl lambda a b . 'echo $(add $a $b)' +echo map multiply +seq 1 4 | list_map lambda a . 'echo $(multiply $a 2)' +echo map minus +seq 1 4 | list_map lambda a . 'echo $(minus $a 2)' +echo map add +seq 1 4 | list_map lambda a . 'echo $(add $a 2)' +echo map divide +seq 1 4 | list_map lambda a . 'echo $(divide $a 2)' +echo list_map mod +seq 1 4 | list_map lambda a . 'echo $(mod $a 2)' echo 'list & head' -list 1 2 3 4 5 | lhead -list {1..2} | append {3..4} | prepend {99..102} +list 1 2 3 4 5 | list_head +list {1..2} | list_append {3..4} | list_prepend {99..102} list {1..2} | unlist -list {1..10} | lhead -list {1..10} | drop 7 -list {1..10} | take 3 -list {1..10} | last -list {1..10} | map λ a . 'echo $(mul $a 2)' +list {1..10} | list_head +list {1..10} | list_drop 7 +list {1..10} | list_take 3 +list {1..10} | list_last +list {1..10} | list_map λ a . 'echo $(multiply $a 2)' id() { λ x . '$x' @@ -37,123 +37,32 @@ foobar() { list {1,2,3} | foobar -echo -n abcdefg | revers_str # gfedcba -echo -n abcdefg | splitc | join , '[' ']' # [a,b,c,d,e,f,g] -echo -n abcdefg | splitc | revers | join , '[' ']' # [g,f,e,d,c,b,a] +echo -n abcdefg | revers_str # gfedcba +echo -n abcdefg | splitc | list_join , # a,b,c,d,e,f,g +echo -n abcdefg | splitc | revers | list_join , # g,f,e,d,c,b,a -echo -n ' abcdefg' | splitc | foldr lambda a b . 'echo $a$b' # gfedcba - -echo 'ls' | try λ cmd status ret . 'echo $cmd [$status]; echo $ret' - -list {1..10} | filter lambda a . '[[ $(mod $a 2) -eq 0 ]] && ret true || ret false' | join , '[' ']' # [2,4,6,8,10] - -function add() { - expr $1 + $2 -} - - -curry add3 add 3 -add3 9 +list {1..10} | filter lambda a . '[[ $(mod $a 2) -eq 0 ]] && ret true || ret false' | list_join , # 2,4,6,8,10 list a b c d | foldl lambda acc el . 'echo -n $acc-$el' -list '' a b c d | foldr lambda acc el . 'if [[ ! -z $acc ]]; then echo -n $acc-$el; else echo -n $el; fi' - seq 1 4 | foldl lambda acc el . 'echo $(($acc + $el))' #1 - 2 - 3 - 4 seq 1 4 | foldl lambda acc el . 'echo $(($acc - $el))' -#1 - 4 - 3 - 2 -seq 1 4 | foldr lambda acc el . 'echo $(($acc - $el))' #1 + (1 + 1) * 2 + (4 + 1) * 3 + (15 + 1) * 4 = 64 - -seq 1 4 | foldl lambda acc el . 'echo $(mul $(($acc + 1)) $el)' - -#1 + (1 + 1) * 4 + (8 + 1) * 3 + (27 + 1) * 2 = 56 -seq 1 4 | foldr lambda acc el . 'echo $(mul $(($acc + 1)) $el)' +seq 1 4 | foldl lambda acc el . 'echo $(multiply $(($acc + 1)) $el)' tup a 1 -tupl $(tup a 1) -tupr $(tup a 1) tup a 1 | tupl tup a 1 | tupr -seq 1 10 | buff lambda a b . 'echo $(($a + $b))' -echo 'XX' -seq 1 10 | buff lambda a b c d e . 'echo $(($a + $b + $c + $d + $e))' - -list a b c d e f | lzip $(seq 1 10) +list a b c d e f | list_zip $(seq 1 10) echo -list a b c d e f | lzip $(seq 1 10) | last | tupr - -arg='[key1=value1,key2=value2,key3=value3]' -get() { - local pidx=$1 - local idx=$2 - local arg=$3 - echo $arg | tr -d '[]' | cut -d',' -f$idx | cut -d'=' -f$pidx -} - -curry get_key get 1 -curry get_value get 2 - -get_key 1 $arg -get_value 1 $arg - -seq 1 3 | map lambda a . 'tup $(get_key $a $arg) $(get_value $a $arg)' - -echo 'ls /home' | try λ cmd status ret . 'echo $cmd [$status]; echo $ret' -echo '/home' | try λ cmd status ret . 'echo $cmd [$status]; echo $ret' +list a b c d e f | list_zip $(seq 1 10) | list_last | tupr seq 1 5 | scanl lambda a b . 'echo $(($a + $b))' -seq 1 5 | scanl lambda a b . 'echo $(($a + $b))' | last +seq 1 5 | scanl lambda a b . 'echo $(($a + $b))' | list_last -seq 2 3 | map lambda a . 'seq 1 $a' | join , [ ] -list a b c | map lambda a . 'echo $a; echo $a | tr a-z A-z' | join , [ ] - -echo 0 | cat - <(curl -s https://raw.githubusercontent.com/ssledz/bash-fun/v1.1.1/src/fun.sh) | \ - map lambda a . 'list $a' | foldl lambda acc el . 'echo $(($acc + 1))' - -echo 0 | cat - <(curl -s curl -s https://raw.githubusercontent.com/ssledz/bash-fun/v1.1.1/src/fun.sh) \ - | foldl lambda acc el . 'echo $(($acc + 1))' - - -factorial() { - fact_iter() { - local product=$1 - local counter=$2 - local max_count=$3 - if [[ $counter -gt $max_count ]]; then - echo $product - else - fact_iter $(echo $counter\*$product | bc) $(($counter + 1)) $max_count - fi - } - - fact_iter 1 1 $1 -} - -factorial_trampoline() { - fact_iter() { - local product=$1 - local counter=$2 - local max_count=$3 - if [[ $counter -gt $max_count ]]; then - res $product - else - call fact_iter $(echo $counter\*$product | bc) $(($counter + 1)) $max_count - fi - } - - with_trampoline fact_iter 1 1 $1 -} - -echo Factorial test - -time factorial 30 -time factorial_trampoline 30 - -# would be error -#time factorial 60 -time factorial_trampoline 60 \ No newline at end of file +seq 2 3 | list_map lambda a . 'seq 1 $a' | list_join , +list a b c | list_map lambda a . 'echo $a; echo $a | tr a-z A-z' | list_join , diff --git a/src/fun.sh b/src/fun.sh index 704ef28..533dd10 100644 --- a/src/fun.sh +++ b/src/fun.sh @@ -361,6 +361,11 @@ revers() { echo "$result" } +# Reverses a string +revers_str() { + splitc | revers | list_join +} + # Removes multiple occurences of # a single character from the beginning # of the list.