Welcome, guest | Sign In | My Account | Store | Cart

An evil equivalent for C expression: 'while x=f()'.

Python, 16 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
import sys
def set(**kw):
    assert len(kw)==1
    
    a = sys._getframe(1)
    a.f_locals.update(kw)
    return kw.values()[0]

#
# sample
#

A=range(10)

while set(x=A.pop()):
    print x

This recipe is an abuse of implementation details which should only be used by debugging tools. So I strongly advise you to not use it in production code.

But it's quite fun and can still be useful for bets.

11 comments

Andrew Dalke 20 years, 8 months ago  # | flag

an alternative using list comprehensions. Again, don't use this in real code.

>>> a = [1, 2, 9, 0]
>>> n = iter(a).next
>>> while [x for x in [n()] if x]:
...     print "I have", x
...
I have 1
I have 2
I have 9
>>>
Raymond Hettinger 18 years, 11 months ago  # | flag

Very nice.

Frank P Mora 18 years, 10 months ago  # | flag

Alternative, safer methods.

Assignment statements and statements in general are anathema to
purely functional language and Lisp programmers but are sometimes
but rarely required. Assignment in an expression can also be achieved
like this:

>>> ## Directly with the expressly designed function:
>>> import __main__
>>> setattr(__main__,'aaa', 300)
>>> aaa
300
>>> ## Or again with the update method and dictionaries
>>> locals().update({'aaa2':200})
>>> aaa2
200
>>> globals().update({'aaa4':500})
>>> aaa4
500
>>> __main__.__dict__.update({'aaa5':600})
>>> aaa5
600
>>> ## Or with the update method and a list of 2-tuples
>>> t= ('bbb',6)
>>> locals().update([t])
>>> bbb
6
>>> ## Or many at once
>>> k= "v1 v2 v3 v4 v5"
>>> v= "red blu grn yel brn"
>>> locals().update(zip( *[ l.split() for l in (k,v)] ))
>>> fmt= "The 5 color codes are %s, %s, %s, %s and %s"
>>> print fmt % (v1,v2,v3,v4,v5)
The 5 color codes are red, blu, grn, yel and brn

Additionally, Dr. David Mertz’s article at

http://gnosis.cx/publish/programming/charming_python_19.html

addresses “Expression Binding” and the Xoltar functional toolkit.
He offers the following:

>>> list_of_list = [[1,2,3],[4,5,6],[7,8,9]]
>>> [car_x for x in list_of_list for car_x in (x[0],)]
[1, 4, 7]

>>> car_x
7
Frank P Mora 18 years, 9 months ago  # | flag

An alternative necessary function definition and example.

Sébastien is offering the utility and conciseness of the valued C code
assignment and *not just* expression assignment as in the preceding
comment of mine. His utility is of much greater value. It requires a
function definition by necessity. Here is an alternate function
definition and yet another example of the usefulness of Sébastien’s
idea in a compression that calculates factorials.

def sv(vnm='', vval=None, obj=__main__):
    setattr(obj, vnm, vval)
    return vval

>>> import __main__
>>> fac =4
>>> [ [ sv('j', j*i) for i in range(2,fac+1) ][-1] for j in (1,) ][0]
24
>>> fac=5
>>> [ [ sv('j', j*i) for i in range(2,fac+1) ][-1] for j in (1,) ][0]
120

The advantage of the setattr function is the variable variable which
allows many assignments is a single pass as in the preceding comment.
It is also the standard method of expression assignment in Python.
Frank P Mora 18 years, 9 months ago  # | flag

But then again.

The list comprehension can set an initial value as in the latter
part of the factorial comprehension in my last comment. After being
set to 1, ‘j’ then becomes the accumulator in the calculation.  If a
list comprehension can set and return values as in a function can they
act as accumulators?

It is completly natural for them to do so.

>>> fac =6
>>> [ [ [ j for j in (j*i,) ][0] for i in range(2,fac+1) ][-1] for j in (1,) ]
[720]

Wow! Andrew Dalke and David Mertz are my new hero’s. It
seems like the awe inspiring list comprehension can do just about
anything. Does Python need function definitions at all much less lambdas?

Maybe just for readability.
Frank P Mora 18 years, 9 months ago  # | flag

Better yet. Steven Bethard pointed out in recipe 436482 that

[j for j in [1] for i in range(2,fac+1) for j in [j*i]]  [-1]

is equivalent but easier to write, read and use.
Yair Chuchem 18 years, 8 months ago  # | flag

won't work in future versions. In future versions of python - list comprehensions won't expose the inner variable.

Connelly Barnes 18 years, 6 months ago  # | flag

Perlishly elegant.

Moe Aboulkheir 18 years, 2 months ago  # | flag

set() is now a builtin. so naming this function "set" is probably not the best idea

Moe Aboulkheir 18 years, 1 month ago  # | flag

Brittle solution. this won't work if you use set() anywhere but the toplevel.

e.g:

def myfunc():
  seq = range(10)
  while set(var=seq.pop()):
    print var
myfunc()

-> NameError: global name 'var' is not defined

the behaviour is also different if the loop variable is already bound:

def myfunc(var='x')
  seq = range(10)
  while set(var=seq.pop()):
    print var
myfunc()

-> equivalent to "while seq.pop(): print 'x'"

Aristotelis Mikropoulos 17 years, 8 months ago  # | flag

Another solution. class DataHolder( object ) : """A simple data holder. """

def __init__( Self , Value = None ) :
    """The constructor.
    """
    Self.Value = Value

def Get( Self ) :
    """Returns the value.
    """
    return Self.Value

def Set( Self , Value ) :
    """Sets the value, as well as
    returns it.
    """
    Self.Value = Value
    return Self.Value
Created by Sébastien Keim on Mon, 26 May 2003 (PSF)
Python recipes (4591)
Sébastien Keim's recipes (24)

Required Modules

Other Information and Tasks