Skip to content
This repository has been archived by the owner on Mar 23, 2023. It is now read-only.

Add builtin filter function #374

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open

Conversation

aisk
Copy link
Contributor

@aisk aisk commented Aug 1, 2017

No description provided.

Copy link
Contributor

@trotterdylan trotterdylan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great! Thanks for working on this PR. I have a few comments.

@@ -248,6 +249,82 @@ func builtinMapFn(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) {
return NewList(result...).ToObject(), nil
}

func builtinFilter(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) {
argc := len(args)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you instead use:

	if raised := checkMethodArgs(f, "filter", args, ObjectType, ObjectType); raised != nil {
		return nil, raised
	}

if fn == None {
if ret, raised := IsTrue(f, item); raised != nil {
return nil, raised
} else if ret {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove the else since the if clause above returns

}
if ret, raised := IsTrue(f, ret); raised != nil {
return nil, raised
} else if ret {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove else

fn := args[0]
l := args[1]
switch {
case l.isInstance(TupleType):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was puzzled about this special case, but then I realized that CPython is doing the same kind of special casing:

>>> class Empty(tuple):
...  def __iter__(self):
...   return self
...  def next(self):
...   raise StopIteration
...
>>> filter(None, Empty([1,2,3]))
(1, 2, 3)

IMO that's bad behavior, but since our goal is compatibility, I guess we need to special case as well. But I think a comment is warranted so that readers of the code understand why we're doing this.

}
if ret, raised := IsTrue(f, ret); raised != nil {
return nil, raised
} else if ret {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove the else

return l, nil
}
var result bytes.Buffer
for _, item := range toStrUnsafe(l).Value() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will iterate over l assuming it's a UTF-8 encoded string. I think what you actually want is to convert this to a []byte and iterate over each byte and WriteByte() below instead of WriteRune().

if fn == None {
if ret, raised := IsTrue(f, item); raised != nil {
return raised
} else if ret {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove else

}
if ret, raised := IsTrue(f, ret); raised != nil {
return raised
} else if ret {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove else

default:
result := make([]*Object, 0)
raised := seqForEach(f, l, func(item *Object) (raised *BaseException) {
if fn == None {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given you do this check a few times I wonder if you should do something like this at the top:

filterFunc := IsTrue
if fn != None {
	filterFunc = func(f *Frame, o *Object) (bool, *BaseException) {
		result, raised := fn.Call(f, Args{o}, nil)
		if raised != nil {
			return false, raised
		}
		return IsTrue(f, result)
	}
}

And then use filterFunc throughout.

{f: "filter", args: wrapArgs(BoolType, NewTuple2(NewInt(0).ToObject(), NewInt(1).ToObject())), want: NewTuple1(NewInt(1).ToObject()).ToObject()},
{f: "filter", args: wrapArgs(IntType, "012"), want: NewStr("12").ToObject()},
{f: "filter", args: wrapArgs(None, newTestList(1, 0, 3)), want: newTestList(1, 3).ToObject()},
{f: "filter", args: wrapArgs(IntType, newTestList("1", "0", "3")), want: newTestList("1", "3").ToObject()},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add tests for subclasses of str and tuple that implement __iter__ to make sure we maintain compatibility with CPython?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi can i add these tests in testing/ directory using Python?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like tests in the Go code because it's easy to measure coverage there. Additional tests in testing/ are fine though.

@aisk
Copy link
Contributor Author

aisk commented Sep 7, 2017

@trotterdylan Updated!

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants