Skip to content

Commit

Permalink
Merge pull request #154 from rokostik/improve-set-functions
Browse files Browse the repository at this point in the history
Improve set functions
  • Loading branch information
refaktor authored Mar 9, 2024
2 parents ffb2521 + 96037b0 commit 44d1c83
Show file tree
Hide file tree
Showing 3 changed files with 167 additions and 80 deletions.
104 changes: 50 additions & 54 deletions evaldo/builtins.go
Original file line number Diff line number Diff line change
Expand Up @@ -4476,7 +4476,7 @@ var builtins = map[string]*env.Builtin{

"sort!": { // **
Argsn: 1,
Doc: "Accepts a block of values and returns maximal value.",
Doc: "Accepts a block or list and sorts in place in ascending order and returns it.",
Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
switch block := arg0.(type) {
case env.Block:
Expand Down Expand Up @@ -5291,49 +5291,22 @@ var builtins = map[string]*env.Builtin{
case env.Block:
switch b2 := arg1.(type) {
case env.Block:
inter := util.UnionOfLists(ps, s1.Series.S, b2.Series.S)
return *env.NewBlock(*env.NewTSeries(inter))
union := util.UnionOfBlocks(ps, s1, b2)
return *env.NewBlock(*env.NewTSeries(union))
default:
return MakeArgError(ps, 2, []env.Type{env.BlockType}, "union")
}
case env.List:
switch secondBlock := arg1.(type) {
case env.Block:
mergedSlice := make([]env.Object, 0)
// Initially fill first block data
for _, v := range s1.Data {
mergedSlice = append(mergedSlice, env.ToRyeValue(v))
}
// If isAvailable is false then it is new value
isAvailable := false
for _, v1 := range secondBlock.Series.S {
isAvailable = false
for _, v2 := range s1.Data {
if env.RyeToRaw(v1) == v2 {
isAvailable = true
break
}
}
// If new value then add in List
if !isAvailable {
mergedSlice = append(mergedSlice, v1)
}
}
// Create list of array
unionSlice := make([]any, 0, len(mergedSlice))
for _, value := range mergedSlice {
unionSlice = append(unionSlice, value)
}
// return List
return *env.NewList(unionSlice)

switch l2 := arg1.(type) {
case env.List:
union := util.UnionOfLists(ps, s1, l2)
return *env.NewList(union)
default:
return MakeArgError(ps, 2, []env.Type{env.BlockType}, "union")
return MakeArgError(ps, 2, []env.Type{env.ListType}, "union")
}
default:
return MakeArgError(ps, 1, []env.Type{env.BlockType, env.ListType}, "union")
}

},
},

Expand All @@ -5354,38 +5327,61 @@ var builtins = map[string]*env.Builtin{
case env.Block:
switch b2 := arg1.(type) {
case env.Block:
inter := util.IntersectLists(ps, s1.Series.S, b2.Series.S)
inter := util.IntersectBlocks(ps, s1, b2)
return *env.NewBlock(*env.NewTSeries(inter))
default:
return MakeArgError(ps, 2, []env.Type{env.BlockType}, "intersect")
}
case env.List:
switch secondBlock := arg1.(type) {
case env.Block:
commonSlice := make([]env.Object, 0)
for _, v1 := range s1.Data {
for _, v2 := range secondBlock.Series.S {
// get matching value in both blocks
if env.RyeToRaw(v2) == v1 {
commonSlice = append(commonSlice, v2)
}
}
}
intersectSlice := make([]any, 0, len(commonSlice))
for _, value := range commonSlice {
intersectSlice = append(intersectSlice, value)
}
// return List
return *env.NewList(intersectSlice)
switch l2 := arg1.(type) {
case env.List:
inter := util.IntersectLists(ps, s1, l2)
return *env.NewList(inter)
default:
return MakeArgError(ps, 2, []env.Type{env.BlockType}, "intersect")
return MakeArgError(ps, 2, []env.Type{env.ListType}, "intersect")
}
default:
return MakeArgError(ps, 1, []env.Type{env.StringType, env.BlockType, env.ListType}, "intersect")
}
},
},

"difference": {
Argsn: 2,
Doc: "Finds the difference (values in first but not in second) of two values.",
Pure: true,
Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
switch s1 := arg0.(type) {
case env.String:
switch s2 := arg1.(type) {
case env.String:
diff := util.DiffStrings(s1.Value, s2.Value)
return *env.NewString(diff)
default:
return MakeArgError(ps, 2, []env.Type{env.StringType}, "difference")
}
case env.Block:
switch b2 := arg1.(type) {
case env.Block:
diff := util.DiffBlocks(ps, s1, b2)
return *env.NewBlock(*env.NewTSeries(diff))
default:
return MakeArgError(ps, 2, []env.Type{env.BlockType}, "difference")
}
case env.List:
switch l2 := arg1.(type) {
case env.List:
diff := util.DiffLists(ps, s1, l2)
return *env.NewList(diff)
default:
return MakeArgError(ps, 2, []env.Type{env.ListType}, "difference")
}
default:
return MakeArgError(ps, 1, []env.Type{env.StringType, env.BlockType, env.ListType}, "difference")
}
},
},

"str": {
Argsn: 1,
Doc: "Turn Rye value to String.",
Expand Down
55 changes: 41 additions & 14 deletions tests/structures.rye
Original file line number Diff line number Diff line change
Expand Up @@ -209,41 +209,68 @@ section "Working with blocks and lists"
group "union"
mold\nowrap ?union
{ { block } }
; sortorder isn't deterministic so far ... think if it should be
{
equal { union { 8 2 } { 1 9 } |length? } 4 ; sortorder isn't deterministic so far ... think if it should be
equal { union { } { 1 9 } |length? } 2
equal { union { 8 2 } { 1 9 } |sort! } { 1 2 8 9 }
equal { union { 1 2 } { } |sort! } { 1 2 }
equal { union { } { 1 9 } |sort! } { 1 9 }
equal { union { } { } } { }

equal { union list { 1 2 } { 1 2 3 4 } } list { 1 2 3 4 }
equal { union list { 1 2 } { 1 } } list { 1 2 }
equal { union list { 1 2 } { } } list { 1 2 }
equal { union list { } { } } list { }
equal { union list { 1 2 } list { 1 2 3 4 } |sort! } list { 1 2 3 4 }
equal { union list { 1 2 } list { 1 } |sort! } list { 1 2 }
equal { union list { 1 2 } list { } |sort! } list { 1 2 }
equal { union list { } list { 1 2 } |sort! } list { 1 2 }
equal { union list { } list { } } list { }
}
group "intersection"
mold\nowrap ?intersection
{ { block } }
{
equal { intersection { 1 3 5 6 } { 2 3 4 5 } } { 3 5 }
equal { intersection { 1 2 3 } { } } { }
equal { intersection { } { 2 3 4 } } { }
equal { intersection { 1 2 3 } { 4 5 6 } } { }
equal { intersection { } { } } { }

equal { intersection list { 1 3 5 6 } { 2 3 4 5 } } list { 3 5 }
equal { intersection list { } { 2 3 4 } } list { }
equal { intersection list { } { } } list { }
equal { intersection list { 1 3 5 6 } list { 2 3 4 5 } } list { 3 5 }
equal { intersection list { 1 2 3 } list { } } list { }
equal { intersection list { } list { 2 3 4 } } list { }
equal { intersection list { 1 2 3 } list { 4 5 6 } } list { }
equal { intersection list { } list { } } list { }
}
group "difference"
mold\nowrap ?difference
{ { block } }
{
equal { difference "abc" "bc" } "a"
equal { difference "abc" "abc" } ""
equal { difference "abc" "" } "abc"
equal { difference "" "" } ""

equal { difference { 1 3 5 6 } { 2 3 4 5 } } { 1 6 }
equal { difference { 1 2 3 } { } } { 1 2 3 }
equal { difference { } { 2 3 4 } } { }
equal { difference { } { } } { }

equal { difference list { 1 3 5 6 } list { 2 3 4 5 } } list { 1 6 }
equal { difference list { 1 2 3 } list { } } list { 1 2 3 }
equal { difference list { } list { 2 3 4 } } list { }
equal { difference list { } list { } } list { }
}

group "unique"
mold\nowrap ?unique
{ { block string list } }
; result order is not deterministic
{
equal { { 3 2 3 5 3 2 } .list .unique .length? } 3 ; result order is not deterministic list { 3 2 5 }
equal { list { 3 2 3 5 3 2 } .unique |sort! } list { 2 3 5 }
; List
equal { unique list { 1 1 2 2 3 } |length? } 3
equal { unique list { 1 1 2 2 } |length? } 2
equal { unique list { 1 1 2 2 3 } |sort! } list { 1 2 3 }
equal { unique list { 1 1 2 2 } |sort! } list { 1 2 }

; Block
equal { unique { 1 1 2 2 3 } |length? } 3
equal { unique { 1 1 2 2 } |length? } 2
equal { unique { 1 1 2 2 3 } |sort! } { 1 2 3 }
equal { unique { 1 1 2 2 } |sort! } { 1 2 }

; String
equal { unique "aabbc" |length? } 3
Expand Down
88 changes: 76 additions & 12 deletions util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package util
import (
"fmt"
"regexp"
"slices"
"strconv"
"strings"

Expand Down Expand Up @@ -203,32 +204,44 @@ func SplitEveryList(s []env.Object, chunkSize int) [][]env.Object {
}

func IntersectStrings(a string, b string) string {
res := ""
set := make(map[rune]bool)
var bu strings.Builder
for _, ch := range a {
if strings.Contains(b, string(ch)) && !strings.Contains(res, string(ch)) {
res = res + string(ch)
if strings.ContainsRune(b, ch) && !set[ch] {
bu.WriteRune(ch)
}
}
return bu.String()
}

func IntersectBlocks(ps *env.ProgramState, a env.Block, b env.Block) []env.Object {
set := make(map[env.Object]bool)
res := make([]env.Object, 0)
for _, v := range a.Series.S {
if ContainsVal(ps, b.Series.S, v) && !set[v] {
res = append(res, v)
}
}
return res
}

func IntersectLists(ps *env.ProgramState, a []env.Object, b []env.Object) []env.Object {
set := make([]env.Object, 0)
for _, v := range a {
if ContainsVal(ps, b, v) && !ContainsVal(ps, set, v) {
set = append(set, v)
func IntersectLists(ps *env.ProgramState, a env.List, b env.List) []any {
set := make(map[any]bool)
res := make([]any, 0)
for _, v := range a.Data {
if slices.Contains(b.Data, v) && !set[v] {
res = append(res, v)
}
}
return set
return res
}

func UnionOfLists(ps *env.ProgramState, a []env.Object, b []env.Object) []env.Object {
func UnionOfBlocks(ps *env.ProgramState, a env.Block, b env.Block) []env.Object {
elementMap := make(map[env.Object]bool)
for _, element := range a {
for _, element := range a.Series.S {
elementMap[element] = true
}
for _, element := range b {
for _, element := range b.Series.S {
elementMap[element] = true
}
mergedSlice := make([]env.Object, 0)
Expand All @@ -238,6 +251,57 @@ func UnionOfLists(ps *env.ProgramState, a []env.Object, b []env.Object) []env.Ob
return mergedSlice
}

func UnionOfLists(ps *env.ProgramState, a env.List, b env.List) []any {
elementMap := make(map[any]bool)
for _, element := range a.Data {
elementMap[element] = true
}
for _, element := range b.Data {
elementMap[element] = true
}
mergedSlice := make([]any, 0)
for element := range elementMap {
mergedSlice = append(mergedSlice, element)
}
return mergedSlice
}

func DiffStrings(a string, b string) string {
set := make(map[rune]bool)
var bu strings.Builder
for _, ch := range a {
if !strings.ContainsRune(b, ch) && !set[ch] {
set[ch] = true
bu.WriteRune(ch)
}
}
return bu.String()
}

func DiffBlocks(ps *env.ProgramState, a env.Block, b env.Block) []env.Object {
set := make(map[env.Object]bool)
res := make([]env.Object, 0)
for _, v := range a.Series.S {
if !ContainsVal(ps, b.Series.S, v) && !set[v] {
set[v] = true
res = append(res, v)
}
}
return res
}

func DiffLists(ps *env.ProgramState, a env.List, b env.List) []any {
set := make(map[any]bool)
res := make([]any, 0)
for _, v := range a.Data {
if !slices.Contains(b.Data, v) && !set[v] {
set[v] = true
res = append(res, v)
}
}
return res
}

func SplitMulti(s string, seps string) []string {
splitter := func(r rune) bool {
return strings.ContainsRune(seps, r)
Expand Down

0 comments on commit 44d1c83

Please sign in to comment.