Avoiding local variable declarations?

D

dpapathanasiou

I have some old Common Lisp functions I'd like to rewrite in Python
(I'm still new to Python), and one thing I miss is not having to
declare local variables.

For example, I have this Lisp function:

(defun random-char ()
"Generate a random char from one of [0-9][a-z][A-Z]"
(if (< 50 (random 100))
(code-char (+ (random 10) 48)) ; ascii 48 = 0
(code-char (+ (random 26) (if (< 50 (random 100)) 65 97))))) ;
ascii 65 = A, 97 = a

My Python version looks like this:

def random_char ():
'''Generate a random char from one of [0-9][a-z][A-Z]'''
if random.randrange(0, 100) > 50:
return chr( random.randrange(0, 10) + 48 ) # ascii 48 = 0
else:
offset = 65 # ascii 65 = A
if random.randrange(0, 100) > 50:
offset = 97 # ascii 97 = a
return chr( random.randrange(0, 26) + offset )

Logically, it's equivalent of the Lisp version.

But is there any way to avoid using the local variable (offset) in the
Python version?
 
C

Chris Mellon

I have some old Common Lisp functions I'd like to rewrite in Python
(I'm still new to Python), and one thing I miss is not having to
declare local variables.

For example, I have this Lisp function:

(defun random-char ()
"Generate a random char from one of [0-9][a-z][A-Z]"
(if (< 50 (random 100))
(code-char (+ (random 10) 48)) ; ascii 48 = 0
(code-char (+ (random 26) (if (< 50 (random 100)) 65 97))))) ;
ascii 65 = A, 97 = a

My Python version looks like this:

def random_char ():
'''Generate a random char from one of [0-9][a-z][A-Z]'''
if random.randrange(0, 100) > 50:
return chr( random.randrange(0, 10) + 48 ) # ascii 48 = 0
else:
offset = 65 # ascii 65 = A
if random.randrange(0, 100) > 50:
offset = 97 # ascii 97 = a
return chr( random.randrange(0, 26) + offset )

Logically, it's equivalent of the Lisp version.

But is there any way to avoid using the local variable (offset) in the
Python version?

Any time you port between languages, it's rarely a good idea to just
convert code verbatim. For example:

import random, string
def random_char():
return random.choice(string.ascii_letters + string.digits)
 
G

Gary Herron

dpapathanasiou said:
I have some old Common Lisp functions I'd like to rewrite in Python
(I'm still new to Python), and one thing I miss is not having to
declare local variables.

For example, I have this Lisp function:

(defun random-char ()
"Generate a random char from one of [0-9][a-z][A-Z]"
(if (< 50 (random 100))
(code-char (+ (random 10) 48)) ; ascii 48 = 0
(code-char (+ (random 26) (if (< 50 (random 100)) 65 97))))) ;
ascii 65 = A, 97 = a

My Python version looks like this:

def random_char ():
'''Generate a random char from one of [0-9][a-z][A-Z]'''
if random.randrange(0, 100) > 50:
return chr( random.randrange(0, 10) + 48 ) # ascii 48 = 0
else:
offset = 65 # ascii 65 = A
if random.randrange(0, 100) > 50:
offset = 97 # ascii 97 = a
return chr( random.randrange(0, 26) + offset )

Logically, it's equivalent of the Lisp version.

But is there any way to avoid using the local variable (offset) in the
Python version?

Yes, you can avoid using offset, but *why*. This certainly won't make
your code cleaner or more easily read/understood/maintainable.

return chr( random.randrange(0, 26) + (97 if random.randrange(0,
100) > 50 else 65)

or

if random.randrange(0, 100) > 50:
return chr( random.randrange(0, 26) + 97)
else:
return chr( random.randrange(0, 26) + 65)

or

return chr( random.randrange(0, 26) + [26,97][random.randrange(0,
100) > 50]

or

... probably other ways can be found ...

but what's wrong with you original code?


Gary Herron



 
D

dpapathanasiou

return chr( random.randrange(0, 26) + (97 if random.randrange(0,
100) > 50 else 65)
or

return chr( random.randrange(0, 26) + [26,97][random.randrange(0,
100) > 50]

Ah, thanks, these are the syntax examples I was looking for.
but what's wrong with you original code?

I come from a functional programming school of thought, where you
avoid local variable declarations if at all possible.
 
D

dpapathanasiou

Any time you port between languages, it's rarely a good idea to just
convert code verbatim. For example:

import random, string
def random_char():
return random.choice(string.ascii_letters + string.digits)

Good point, and thanks for the idiomatic Python example (I like the
conciseness); I'm still wrapping my mind around how Python statements
should be constructed.
 
A

Andrew Koenig

return chr( random.randrange(0, 26) + [26,97][random.randrange(0,
100) > 50]

return chr(random.randrange(0, 26) + (97 if random.randrange(0,100) > 50
else 26))
 
A

Arnaud Delobelle

dpapathanasiou said:
I come from a functional programming school of thought, where you
avoid local variable declarations if at all possible.

Holding on to this principle won't help you write nice Python code :)

Although you will acquire lots of clever-looking tricks.
 
A

Aahz

I come from a functional programming school of thought, where you
avoid local variable declarations if at all possible.

Python is *so* not a functional programming language. There are a number
of functional programming features available, but trying to push your
Python programs into primarily a functional style will result in
significantly poorer Python programs. Find another language if
functional programming is critical to your style.
 
P

Paul McGuire

I have some old Common Lisp functions I'd like to rewrite in Python
(I'm still new to Python), and one thing I miss is not having to
declare local variables.
For example, I have this Lisp function:
(defun random-char ()
 "Generate a random char from one of [0-9][a-z][A-Z]"
 (if (< 50 (random 100))
     (code-char (+ (random 10) 48)) ; ascii 48 = 0
     (code-char (+ (random 26) (if (< 50 (random 100)) 65 97))))) ;
ascii 65 = A, 97 = a
My Python version looks like this:
def random_char ():
   '''Generate a random char from one of [0-9][a-z][A-Z]'''
   if random.randrange(0, 100) > 50:
       return chr( random.randrange(0, 10) + 48 ) # ascii 48 = 0
   else:
       offset = 65 # ascii 65 = A
       if random.randrange(0, 100) > 50:
           offset = 97 # ascii 97 = a
       return chr( random.randrange(0, 26) + offset )
Logically, it's equivalent of the Lisp version.
But is there any way to avoid using the local variable (offset) in the
Python version?

Any time you port between languages, it's rarely a good idea to just
convert code verbatim. For example:

import random, string
def random_char():
    return random.choice(string.ascii_letters + string.digits)- Hide quoted text -

- Show quoted text -

Not quite. The original Lisp function first flips a coin to see if a
digit or alpha should be returned. If alpha, then flips a coin again
to see if upper or lower case should be returned. The alpha branch
could be collapsed into just returning a random selection from [A-Za-
z], but if you combine the alpha and numeric branches, you have less
than a 1/3 chance of getting a digit, vs. the 50-50 chance of the
original Lisp code.

Try this:

import random
import string
coinflip = lambda : int(random.random()*2)
if coinflip():
return random.choice(string.digits)
else:
return random.choice(string.ascii_letters)

or just:

return random.choice( (string.digits, string.ascii_letters)[coinflip
()] )

-- Paul
 
P

Paul McGuire

Paul McGuire:


I warmly suggest you to use this instead:
randrange(2)

Bye,
bearophile

Really? Looking at randrange, it sure seems to do a lot of work in
pursuit of handling all possible cases for specifying range
boundaries, step values, etc. In fact, I think flipping a coin is a
common enough task that it might even merit an addition to the random
module API. It certainly would allow us to get rid of all of these
variations on the theme, such as "if randrange(100) > 50", or
"random.choice([True,False])". For that matter, my own obscure int
(random.random() * 2) is better implemented using a direct comparison
to random.random(), no need for multiplication, or int calls or
anything else. Having an explicit method would also help prevent
minor erroneous bugs like "if random.random() > 0.5", given that the
range of random.random() is (0.0, 1.0] (that is, the lower half would
be 0 inclusive to 0.5 exclusive, and the upper half 0.5 inclusive to
1.0 exclusive).

If I would change anything, I think I prefer the name coin_toss over
coinflip.

So:

coin_toss = lambda : random.random() >= 0.5

-- Paul
 
B

bearophileHUGS

Paul McGuire:
Really?  Looking at randrange, it sure seems to do a lot of work in
pursuit of handling all possible cases for specifying range
boundaries, step values, etc.

Well, randrange is the simpler thing to read and understand here, and
maybe the one less likely to get wrong too.
But I understand your concerns. The solution is to write a C function
in the random module that implements the randrange in an efficient
way.

Bye,
bearophile
 
M

Mark Wooding

Chris Mellon said:
Any time you port between languages, it's rarely a good idea to just
convert code verbatim. For example:

import random, string
def random_char():
return random.choice(string.ascii_letters + string.digits)

Note that this code doesn't preserve the output distribution of the
original, for which once expects half the characters to be numeric. I
don't know if that's relevant; in fact I suspect that the original was
buggy.

-- [mdw]
 
P

Paul McGuire

Chris Mellon said:
Any time you port between languages, it's rarely a good idea to just
convert code verbatim. For example:
import random, string
def random_char():
    return random.choice(string.ascii_letters + string.digits)

Note that this code doesn't preserve the output distribution of the
original, for which once expects half the characters to be numeric.  I
don't know if that's relevant; in fact I suspect that the original was
buggy.

-- [mdw]

I couldn't have said it better myself. Well, if not better, at least
earlier...
(http://groups.google.com/group/comp.lang.python/msg/580779bf57e4d3a0?
hl=en)

-- Paul
 
J

Jorgen Grahn

I come from a functional programming school of thought, where you
avoid local variable declarations if at all possible.

I'm not sure that's universal. Using Standard ML at Uni, it was often
useful to use "let name = expr in expr" (or whatever the syntax was)
to simplify an expression. Directly borrowed from mathematics, I
assume.

'name' is not a variable, of course; there are no variables in
functional programming. Can't remember what it's called -- named
expression, maybe?

I think I use local names in Python about as much as I did in SML.

/Jorgen
 
M

Marc 'BlackJack' Rintsch

On Thu, 13 Nov 2008 12:49:02 -0800 (PST), dpapathanasiou


I'm not sure that's universal. Using Standard ML at Uni, it was often
useful to use "let name = expr in expr" (or whatever the syntax was) to
simplify an expression. Directly borrowed from mathematics, I assume.

That's (also?) Haskell syntax and I agree that it is useful to write
readable code.
'name' is not a variable, of course; there are no variables in
functional programming. Can't remember what it's called -- named
expression, maybe?

I think it's called variable and works like variables work in
mathematics, i.e. you can assign only once. Not such illogical crap like
``a = a + 1`` which must be obviously false unless 1 is defined as the
neutral element for the definition of ``+`` here. :)

Ciao,
Marc 'BlackJack' Rintsch
 
S

Steven D'Aprano

Not such illogical crap like
``a = a + 1`` which must be obviously false unless 1 is defined as the
neutral element for the definition of ``+`` here.

I don't quite know what you mean by "neutral element". I think you mean
the identity element under addition, which is zero, but that doesn't work:

0 = 0 + 1

Obviously not.

I think the only workable answer might be a = infinity, but that has two
problems: (1) infinity isn't a valid integer or real number (although
there is a valid float representing infinity on many platforms); and (2)
which infinity? Aleph 0, aleph 1, c, ... There's an infinite number of
them.
 
G

Gabriel Genellina

En Mon, 17 Nov 2008 22:18:51 -0200, Steven D'Aprano
I don't quite know what you mean by "neutral element". I think you mean
the identity element under addition, which is zero, but that doesn't
work:

0 = 0 + 1

Obviously not.

Perhaps you didn't read carefully the above post? He said "unless *1* is
defined as the neutral element for ``+``"
Calling "1" the identity of the "+" operation, or calling "+" an operation
having "1" as its identity element is, errr, uncommon at least, but
perfectly valid...
 
R

Russ P.

En Mon, 17 Nov 2008 22:18:51 -0200, Steven D'Aprano





Perhaps you didn't read carefully the above post? He said "unless *1* is
defined as the neutral element for ``+``"
Calling "1" the identity of the "+" operation, or calling "+" an operation
having "1" as its identity element is, errr, uncommon at least, but
perfectly valid...

def __add__(self, other): return self + other - 1

Don't tell me this is not "Pythonic"!
 

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

Forum statistics

Threads
473,763
Messages
2,569,562
Members
45,038
Latest member
OrderProperKetocapsules

Latest Threads

Top