By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
455,548 Members | 1,496 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 455,548 IT Pros & Developers. It's quick & easy.

Working with a list in a more „pythonic“ way

P: n/a
Hi all,

I would like to find a more pythonic way of solving the following:

Having a string consisting of letters only, find out the total sound
score of the string. The sound score is calculated as the sum of the
transition scores between the characters in that string. The transition
scores are stored in a 26 x 26 matrix. I.e. the transition A -> F would
have the score soundScoreMatrix[0][5].

I have come up with the following solution, but I hope some of you might
suggest a more _functional_ (reduce and map) way of diong it.

n = 0 # the final score of the string

for i in range(len(phrase)):
try:
n += soundScoreMatrix[ord(x[i]) - 65][ord(x[i + 1]) - 65]
except IndexError:
pass

I was thinking about using "reduce", but that would not work as the
input and output of the function I would use are different (string input,
integer output).

Many thanks in advance,
Nicky
Jul 18 '05 #1
Share this Question
Share on Google+
12 Replies


P: n/a
Nickolay Kolev wrote:
...The sum of transition scores between the characters in a string.
The transition scores are stored in a 26 x 26 matrix. I.e. the
transition A -> F would have the score soundScoreMatrix[0][5].
This is the part I'd change:

# Setup: transitionScore['AF'] = soundScoreMatrix[0][5].
transitionScore = {}
for i in range(26):
first = chr(65+i)
row = soundScoreMatrix[i]
for j in range(26):
transitionScore[first + chr(65+j)] = row[j]
n = 0 # the final score of the string
for i in range(len(phrase)):
try:
n += soundScoreMatrix[ord(x[i]) - 65][ord(x[i + 1]) - 65]
except IndexError:
pass


def scoreit(phrase):
score = 0
for i in range(len(phrase) - 1):
score += transitionScore.get(phrase[i : i+2], 0)
return score

But if you insist on a more functional style:

def scoreterse(phrase):
return sum([transitionScore.get(phrase[i : i+2], 0)
for i in range(len(phrase) - 1)])

which doesn't really look clearer to me. I'd be tempted to insert
a line:
phrase = phrase.upper()

at the top of the function (scoreit or scoreterse) you use, depending,
of course, on the reliability of the source of your data.

--
-Scott David Daniels
Sc***********@Acm.Org
Jul 18 '05 #2

P: n/a
il 4 Apr 2004 10:48:28 GMT, Nickolay Kolev <nm*****@uni-bonn.de> ha
scritto::

I have come up with the following solution, but I hope some of you might
suggest a more _functional_ (reduce and map) way of diong it.

n = 0 # the final score of the string

for i in range(len(phrase)):
try:
n += soundScoreMatrix[ord(x[i]) - 65][ord(x[i + 1]) - 65]
except IndexError:
pass

I was thinking about using "reduce", but that would not work as the
input and output of the function I would use are different (string input,
integer output).


why not? you just need to say to reduce to use a 0 as the first
parameter:

say:

def get_val(x):
# gets the value from SSMatrix, now dumb
return ord(x)
reduce(lambda a,b: a+get_val(b),list('blablabla'),0) #909

Jul 18 '05 #3

P: n/a
Nickolay Kolev wrote:
I was thinking about using "reduce", but that would not work as the
input and output of the function I would use are different (string input,
integer output).


reduce() does not impose such a restriction. Optimized for minimal
robustness and readability (but working, I hope, and functional in the
_narrow_ sense):

def calcScore(phrase):
return reduce(
lambda (sigma, last), i: (sigma + soundScoreMatrix[last][i], i),
map(lambda c: ord(c)-65, phrase[1:]), (0, ord(phrase[0])-65))[0]

Seriously, trying to apply a particular style does often result in bad
design. Either go with Scott Daniels' approach or debug your initial idea
to something that works. Both ways are more "pythonic" than the above.

Peter
Jul 18 '05 #4

P: n/a
> def get_val(x):
# gets the value from SSMatrix, now dumb
return ord(x)


The point is to sum the transitions *between* the characters and the
matrix only contains those scores. So "getting the value from SSMatrix"
for a single character makes no sense (and is impossible).

Nicky
Jul 18 '05 #5

P: n/a
> This is the part I'd change:

# Setup: transitionScore['AF'] = soundScoreMatrix[0][5].
transitionScore = {}
for i in range(26):
first = chr(65+i)
row = soundScoreMatrix[i]
for j in range(26):
transitionScore[first + chr(65+j)] = row[j]
That would create another list containing all possible transitions,
right? Are you doing this just to avoid indexing the matrix later when
going over the phrase letter by letter?
def scoreit(phrase):
score = 0
for i in range(len(phrase) - 1):
score += transitionScore.get(phrase[i : i+2], 0)
return score

But if you insist on a more functional style:

def scoreterse(phrase):
return sum([transitionScore.get(phrase[i : i+2], 0)
for i in range(len(phrase) - 1)])


It is just those *for i in range(len(x))* things I was hoping to get rid
of.

As mentioned, I have a solution that works and produces the expected
results. I wanted to see if there was a clearer way to write what I had
in mind. You see, writing the factorial function using "reduce" was a
breakthrough for me... :-)

Thanks for your reply!

Nicky
Jul 18 '05 #6

P: n/a
Nickolay Kolev <nm*****@uni-bonn.de> wrote in message news:<20********************@news.rhrz.uni-bonn.de>...

Having a string consisting of letters only, find out the total sound
score of the string. The sound score is calculated as the sum of the
transition scores between the characters in that string. The transition
scores are stored in a 26 x 26 matrix. I.e. the transition A -> F would
have the score soundScoreMatrix[0][5].


My attempt here:

import operator
soundScoreMatrix = [[.1,]*26]*26 # sample score matrix
phrase = 'abracadabra'
cap_phrase = phrase.upper()
indices = [ord(c)-65 for c in cap_phrase]
scores = [soundScoreMatrix[indices[i-1]][indices[i]]
for i in range(1, len(indices))]
sum = reduce(operator.add, scores)
print sum

regards,

Hung Jung
Jul 18 '05 #7

P: n/a

"Nickolay Kolev" <nm*****@uni-bonn.de> wrote in message
news:20********************@news.rhrz.uni-bonn.de...
I would like to find a more pythonic way of solving the following: .... I have come up with the following solution, but I hope some of you might
suggest a more _functional_ (reduce and map) way of diong it.


For anti-reduce Pythoneers, those two desires are contradictory;-) My
take: Pythonic is first correct, then about as clear as possible what is
being done and why that gives the correct answer. Both your table lookup
and Daniels dict lookup are immediately obvious and thus qualify to me.
Both handle the switch between chars and pairs of chars about as smoothly
as possible (except for your unnecessary try: except: as a substitute for
subtracting 1 from the length).

Rewritten, your code is

score = 0
for i in range(len(phrase)-1):
score += soundScoreMatrix[ord(x[i]) - 65][ord(x[i + 1]) - 65]

Once written thusly, the reduce form is obvious (but obviously not tested):

reduce(lambda score, i: score + soundScoreMatrix[ord(x[i]) - 65][ord(x[i +
1]) - 65],
range(len(phrase)-1), 0)

but aside from any didactic value this has, I prefer, in this case, the
written-out loop.

Terry J. Reedy


Jul 18 '05 #8

P: n/a
[second try, python.org refused the message when the header contained
non-ascii characters. As an aside, does anyone know how to configure
mutt to quote non-ascii characters in headers?]

First, you might want to map the letters to numbers all at once:
phrase = [ord(x) - 65 for x in phrase]
then you could use an iterator to give the pairs:
def pairs(seq):
seq = iter(seq)
a = seq.next()
for j in seq:
yield a, j
a = j
Example usage:
list(pairs(range(5)))

[(0, 1), (1, 2), (2, 3), (3, 4)]

So now you can write
n = 0
for first, second in pairs(phrase):
n += soundScore[first][second]
taking care of IndexError as above. You could also make soundScore a
dictionary, which I think was discussed elsewhere.
n = 0
for p in pairs(phrase):
n += soundScore.get(p, 0)
or
n = sum([soundScore.get(p, 0) for p in pairs(phrase)])

Jeff

Jul 18 '05 #9

P: n/a
In article <ma**************************************@python.o rg>,
"Terry Reedy" <tj*****@udel.edu> wrote:
Rewritten, your code is

score = 0
for i in range(len(phrase)-1):
score += soundScoreMatrix[ord(x[i]) - 65][ord(x[i + 1]) - 65]

Once written thusly, the reduce form is obvious (but obviously not tested):

reduce(lambda score, i: score + soundScoreMatrix[ord(x[i]) - 65][ord(x[i +
1]) - 65],
range(len(phrase)-1), 0)

but aside from any didactic value this has, I prefer, in this case, the
written-out loop.


I prefer the sum form -- if sum() isn't good for this example, what is
it there for at all? I also don't like seeing the magic number 65
without some sort of explanation. And how come you're using x in one
place and phrase in another?

ords = [ord(c) - ord('A') for c in phrase.upper()]
score = sum([soundScoreMatrix[ords[i]][ords[i+1]]
for i in range(len(phrase)-1)
if 0 <= ords[i] < 26 and 0 <= ords[i+1] < 26])

--
David Eppstein http://www.ics.uci.edu/~eppstein/
Univ. of California, Irvine, School of Information & Computer Science
Jul 18 '05 #10

P: n/a
Nickolay Kolev wrote:
I have come up with the following solution, but I hope some of you might
suggest a more _functional_ (reduce and map) way of diong it.

n = 0 # the final score of the string

for i in range(len(phrase)):
try:
n += soundScoreMatrix[ord(x[i]) - 65][ord(x[i + 1]) - 65]
except IndexError:
pass

I was thinking about using "reduce", but that would not work as the
input and output of the function I would use are different (string input,
integer output).

IMO, the clearest functional way to do it (where, for the sake of
argument, we're defining functional not to include list comps) is to
use offset slices. The idea is to use one slice of phrase to
represent the first index, and a second slice, offset by one, to
represent the second index in the matrix. I'd also map the phrase
into integers beforehand. Take a look:

iphrase = map(lambda x:ord(x)-65,phrase)
n = sum(map(lambda a,b: soundScoreMatrix[a][b], iphrase[:-1], iphrase[1:]))

If you want to use reduce instead of sum (say you're not yet at Python
2.3), then replace "sum(x)" with "reduce(operator.add,x)".
--
CARL BANKS http://www.aerojockey.com/software
"If you believe in yourself, drink your school, stay on drugs, and
don't do milk, you can get work."
-- Parody of Mr. T from a Robert Smigel Cartoon
Jul 18 '05 #11

P: n/a
Peter Otten <__*******@web.de> wrote in message news:<c4*************@news.t-online.com>...

def calcScore(phrase):
return reduce(
lambda (sigma, last), i: (sigma + soundScoreMatrix[last][i], i),
map(lambda c: ord(c)-65, phrase[1:]), (0, ord(phrase[0])-65))[0]


That's it! Great! "Tail call" in functional programming for emulating
loop variables in imperative programming (the "last" variable in this
case.) However, whenever functional languages get to the point of
using tail calls, I think they get into a sad state of affair. It's
basically using functional language to do imperative programming. In
imperative languages you would have a sequence of lines for the
assignments, and in functional language you pile them up as arguments
to the left in the tail call, effectively emulating states. It's like
writing a program horizontally. :)

Hung Jung
Jul 18 '05 #12

P: n/a
>> # Setup: transitionScore['AF'] = soundScoreMatrix[0][5].
transitionScore = {}
for i in range(26):
first = chr(65+i)
row = soundScoreMatrix[i]
for j in range(26):
transitionScore[first + chr(65+j)] = row[j]

That would create another list containing all possible transitions,
right? Are you doing this just to avoid indexing the matrix later when
going over the phrase letter by letter?


He's creating a dictionary. And yes, he's using it to avoid indexing
into the matrix. When you are dealing with some sort of object that is
indexed by pairs (or triples or quads) of objects, it is quite Pythonic
to index that object by those same blocks.
import this

The Zen of Python, by Tim Peters
Jul 18 '05 #13

This discussion thread is closed

Replies have been disabled for this discussion.