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 12 1431
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
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
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
> 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
> 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
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
"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
[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
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
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
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
>> # 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 This discussion thread is closed Replies have been disabled for this discussion. Similar topics
reply
views
Thread by Hans Maurer |
last post: by
|
7 posts
views
Thread by codeslayer |
last post: by
|
5 posts
views
Thread by caviar |
last post: by
|
2 posts
views
Thread by Don |
last post: by
|
6 posts
views
Thread by Nx |
last post: by
|
4 posts
views
Thread by Jim Langston |
last post: by
| | |
5 posts
views
Thread by =?Utf-8?B?UVNJRGV2ZWxvcGVy?= |
last post: by
| | | | | | | | | | |