Can this be written more concisely in a functional style

B

Ben Finney

1)
def f(xs):
for x in xs:
if test(x): return True
return False

Makes it obvious that there is a way for the iternation to end early.
2)
return True in map(test,xs)

Strongly implies ("foo in list", "map()") that the entire list will be
iterated. Any other behaviour would be unexpected to the person reading
the code.
I know that I can do (2), but it operates on the whole list and the
original may break out early. I want the efficiency of (1), but the
conciseness of (2).

I think that in seeking to make it more concise, you're also seeking to
make it less obvious. Anything that has the semantics of "loop over the
whole list" in a single statement, isn't going to help people understand
that a common case is for the iteration to end early. Which is probably
a good reason for it not to appear.

If you want to hide the algorithm, do so inside a helper function. Then
you have consision in the places where you're actually using it, and
explicit semantics where the algorithm is implemented.
 
B

Ben Finney

If you want to hide the algorithm, do so inside a helper function.
Then you have consision in the places where you're actually using it,
and explicit semantics where the algorithm is implemented.

On second look, you appear to *be* putting this in a helper function,
presumably for the purpose of hiding the implementation. If so, it's a
good thing that the implementation is explicit here -- anyone who goes
looking into this function wants it obvious how it works.
 
M

MetalOne

1)
def f(xs):
for x in xs:
if test(x): return True
return False

I know that I can do (2), but it operates on the whole list and the original
may break out early. I want the efficiency of (1), but the conciseness of (2).

2)
return True in map(test,xs)
 
B

Bengt Richter

1)
def f(xs):
for x in xs:
if test(x): return True
return False

I know that I can do (2), but it operates on the whole list and the original
may break out early. I want the efficiency of (1), but the conciseness of (2).

2)
return True in map(test,xs)
That's not quite the same, unless you guarantee that test(x)==bool(test(x))==True when
test(x) is logically true, and never returns True otherwise. (E.g., what if test were
def test(x): return x ? f(range(5)) will give you a True when you hit 1 but, map(test,range(5))
will just be the numbers, and there will be no True in that).

I guess with generator expressions you will soon be able to write

def f(xs): return True in (bool(test(x)) for x in xs)

We can fake the generator expression and a test that will show us how far it went, to see...
...
(Ok, that does guarantee a bool, but some other test might conceivably not).
... for x in seq: yield bool(fun(x))
...
a
b
c
1
2
3
True

Regards,
Bengt Richter
 
G

Georgy Pruss

Something like
return any( test, xs )
or
return xs.any( test )
?

--
Georgy Pruss
E^mail: 'ZDAwMTEyMHQwMzMwQGhvdG1haWwuY29t\n'.decode('base64')


| 1)
| def f(xs):
| for x in xs:
| if test(x): return True
| return False
|
| I know that I can do (2), but it operates on the whole list and the original
| may break out early. I want the efficiency of (1), but the conciseness of (2).
|
| 2)
| return True in map(test,xs)
 
M

MetalOne

Maybe my post was not clear.
I want a means to test if there exists an element in the list that
satisfies a predicate.

Actually, when I word it that way, I guess what I want is PEP 289,
universal and existential qualifiers.

I guess I'll have to wait.
 
A

Alex Martelli

MetalOne said:
1)
def f(xs):
for x in xs:
if test(x): return True
return False

I know that I can do (2), but it operates on the whole list and the
original
may break out early. I want the efficiency of (1), but the conciseness of
(2).

2)
return True in map(test,xs)

[2] is quite different [1] in terms of semantics, of course. [1] will
accept any true (non-false) result, such as 23 or 'foo', [2] won't. If
[2]'s semantics are what you want,

return True in itertools.imap(test, xs)

should give you exactly what you require. Otherwise, you may want to
ensure a 'bool' is further applied, either by using a lambda or by
nesting two imap calls.


Alex
 
J

Jeremy Fincher

Maybe my post was not clear.
I want a means to test if there exists an element in the list that
satisfies a predicate.

Sure there is. The code that you showed was an excellent way to do
so.
Actually, when I word it that way, I guess what I want is PEP 289,
universal and existential qualifiers.

I guess I'll have to wait.

Why? Why not just stuff the code you wrote into an appropriately
named function and use that?

Anyway, here are more efficient implementations:

def any(p, seq):
"""Returns true if any element in seq satisfies predicate p."""
for elt in itertools.ifilter(p, seq):
return True
else:
return False

def all(p, seq):
"""Returns true if all elements in seq satisfy predicate p."""
for elt in itertools.ifilterfalse(p, seq):
return False
else:
return True

Jeremy
 
M

Michele Simionato

Anyway, here are more efficient implementations:

def any(p, seq):
"""Returns true if any element in seq satisfies predicate p."""
for elt in itertools.ifilter(p, seq):
return True
else:
return False

def all(p, seq):
"""Returns true if all elements in seq satisfy predicate p."""
for elt in itertools.ifilterfalse(p, seq):
return False
else:
return True

Jeremy

This is a perfect example of why I dislike the "else" clause. I
would code this as

def any(p, seq):
"""Returns true if any element in seq satisfies predicate p."""
for elt in itertools.ifilter(p, seq):
return True
return False

def all(p, seq):
"""Returns true if all elements in seq satisfy predicate p."""
for elt in itertools.ifilterfalse(p, seq):
return False
return True

Since the "else" is unnecessary, it disturbs me, I get confused,
I don't see why it is used (there is no break in the loop) and the
code becomes much harder to read, for me. OTOH I am sure 99% of
people would say "look, it is obvious, if elt is in the output
of ifilter it will return True, else False (viceversa in the
second case)". But may brain sees that the "else" is unncessary
and immediately it is disturbed by th redundance.
Am I the only one? ;)

Michele
 
D

Dennis Lee Bieber

Michele Simionato fed this fish to the penguins on Tuesday 18 November
2003 07:14 am:
This is a perfect example of why I dislike the "else" clause. I
would code this as
To one extant, for these examples in particular (due to the
shortness), I don't like either.

Think its my old "structured programming" indoctrination coming
through -- the "one way in, one way out" mentality.
def any(p, seq):
"""Returns true if any element in seq satisfies predicate p."""
for elt in itertools.ifilter(p, seq):
return True
return False

def all(p, seq):
"""Returns true if all elements in seq satisfy predicate p."""
for elt in itertools.ifilterfalse(p, seq):
return False
return True
I'd be wasting a local with

def any(p, seq):
"""ibid"""
a = False
for elt in itertools.ifilter(p, seq):
a = True
return a

and similar for the all()

--
 
A

Alexander Schmolck

Dennis Lee Bieber said:
I'd be wasting a local with

def any(p, seq):
"""ibid"""
a = False
for elt in itertools.ifilter(p, seq):
a = True break
return a

You're not just wasting a local.

'as
 
A

Alexander Schmolck

Am I the only one? ;)

I think such code is likely to fool people into thinking that the else part of
loops is only executed if no single iteration takes place.

'as
 
A

Alexander Schmolck

Sure there is. The code that you showed was an excellent way to do
so.


Why? Why not just stuff the code you wrote into an appropriately
named function and use that?

Anyway, here are more efficient implementations:

def any(p, seq):
"""Returns true if any element in seq satisfies predicate p."""
for elt in itertools.ifilter(p, seq):
return True
else:
return False

Another alternative (also works for python2.2, but is likely to be slower):

def some(predicate, *seqs):
iterables = map(iter, seqs)
try:
while 1:
boo = predicate(*[iterable.next() for iterable in iterables])
if boo: return boo
except StopIteration: return False

'as
 
A

Anton Vredegoor

def any(p, seq):
"""Returns true if any element in seq satisfies predicate p."""
for elt in itertools.ifilter(p, seq):
return True
return False

def all(p, seq):
"""Returns true if all elements in seq satisfy predicate p."""
for elt in itertools.ifilterfalse(p, seq):
return False
return True

Since the "else" is unnecessary, it disturbs me, I get confused,
I don't see why it is used (there is no break in the loop) and the
code becomes much harder to read, for me. OTOH I am sure 99% of
people would say "look, it is obvious, if elt is in the output
of ifilter it will return True, else False (viceversa in the
second case)". But may brain sees that the "else" is unncessary
and immediately it is disturbed by th redundance.
Am I the only one? ;)

Well, don't the multiple returns disturb you? I'd suggest this but
probably it's too clever:

from itertools import islice,ifilter

def any(predicate,seq):
return bool(list(islice(ifilter(predicate,seq),1)))

def test():
xs = 'abc123def456'
def fun(x):
print x
return x == '3'
print any(fun,xs)

if __name__=='__main__':
test()

Anton
 
M

MetalOne

My intention was that test() return True or False.
So True in itertools.imap(test, xs) is what I was looking for.

I also like the any() function suggested above.
def any(p, seq):
"""Returns true if any element in seq satisfies predicate p."""
for elt in itertools.ifilter(p, seq):
return True
return False


In the itertools.imap version does
<True in> test against a continually growing list, ie.
True in [False]
True in [False, False]
True in [False, False, False]
True in [False, False, False, True]

or does it just apply <in> to the next generated element.

Is the itertools.imap version as efficient as the for loop version?


Thanks.
 
J

Jeremy Fincher

This is a perfect example of why I dislike the "else" clause. I
would code this as

def any(p, seq):
"""Returns true if any element in seq satisfies predicate p."""
for elt in itertools.ifilter(p, seq):
return True
return False

def all(p, seq):
"""Returns true if all elements in seq satisfy predicate p."""
for elt in itertools.ifilterfalse(p, seq):
return False
return True

Since the "else" is unnecessary, it disturbs me, I get confused,
I don't see why it is used (there is no break in the loop)

It's used because the for loop is effectively serving as an if
statement. Iterators have no __nonzero__ method -- you can't simply
bool() them. So I use a for loop like a if statement, and throw the
else in there to emphasize that usage. The for loop will never
iterate; either its body executes or it doesn't.

An alternative way to code this would be:

def any(p, seq):
try:
itertools.ifilter(p, seq).next()
return True
except StopIteration:
return False

but I find that less clear.

I also use else because it puts my return statements at the same level
of indentation, which I find more readable, since logically they're
equivalent.
Am I the only one? ;)

One can only hope! <wink>

Jeremy
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,434
Messages
2,571,690
Members
48,796
Latest member
Greg L.

Latest Threads

Top