Velocity Reviews > Re: sequence multiplied by -1

# Re: sequence multiplied by -1

Thomas Jollans
Guest
Posts: n/a

 09-25-2010
On Saturday 25 September 2010, it occurred to Yingjie Lan to exclaim:
> Hi,
>
> I noticed that in python3k, multiplying a sequence by a negative integer is
> the same as multiplying it by 0, and the result is an empty sequence. It
> seems to me that there is a more meaningful symantics.

Um...

for every list l and integer n >= 0:
len(l*n) == len(l)*n

Multiplying a list by a negative integer should produce a list of negative
length, which does not exist. IMHO, the only correct behaviour would be to
raise an exception, though one could argue that there are practical benefits
for the operation to succeed for any integer operand.

>
> Simply put, a sequence multiplied by -1 can give a reversed sequence.

For that, we have slicing. A negative step value produces a reverse slice of
the list. You can't argue that this makes sense, can you

>>> [1,2,3,4][::-1]

[4, 3, 2, 1]
>>> [1,2,3,4][::-2]

[4, 2]
>>>

Steven D'Aprano
Guest
Posts: n/a

 09-26-2010
On Sat, 25 Sep 2010 13:45:11 +0200, Thomas Jollans wrote:

> On Saturday 25 September 2010, it occurred to Yingjie Lan to exclaim:
>> Hi,
>>
>> I noticed that in python3k, multiplying a sequence by a negative
>> integer is the same as multiplying it by 0, and the result is an empty
>> sequence. It seems to me that there is a more meaningful symantics.

>
> Um...
>
> for every list l and integer n >= 0:
> len(l*n) == len(l)*n
>
> Multiplying a list by a negative integer should produce a list of
> negative length, which does not exist.

Look at the domain of your invariance. It says nothing about negative n,
and nor should it.

> IMHO, the only correct behaviour
> would be to raise an exception, though one could argue that there are
> practical benefits for the operation to succeed for any integer operand.

Since lists of negative length don't exist, we're free to set the
invariance to something which has a less restrictive domain and is more
useful than raising an exception:

for every list l and integer n:
len(l*n) == len(l)*max(0, n)

>> Simply put, a sequence multiplied by -1 can give a reversed sequence.

>
> For that, we have slicing. A negative step value produces a reverse
> slice of the list. You can't argue that this makes sense, can you
>
>>>> [1,2,3,4][::-1]

> [4, 3, 2, 1]
>>>> [1,2,3,4][::-2]

> [4, 2]

Why does it not make sense?

I'm more concerned that the simple idiom:

# list L must have at least 5 items, so extend it with None if needed
L.extend([None]*(5 - len(L))

would suddenly break with this proposal.

--
Steven

John Nagle
Guest
Posts: n/a

 09-26-2010
On 9/25/2010 4:45 AM, Thomas Jollans wrote:
> On Saturday 25 September 2010, it occurred to Yingjie Lan to exclaim:
>> Hi,
>>
>> I noticed that in python3k, multiplying a sequence by a negative integer is
>> the same as multiplying it by 0, and the result is an empty sequence. It
>> seems to me that there is a more meaningful symantics.

The concept that the multiply operator should be overloaded to
do something l33t on sequences was a Python design mistake. It

x = 10
y = "10"

x*2
20
y*2
'1010'
int(y*2)
1010
int(y)*2
20

That's awful. It's the kind of thing a novice C++
programmer would write shortly after they discovered operator

Now, ask yourself, when you get a numeric value back from
a database, is it an integer or a string? How about the CSV
module? Are you sure?

John Nagle

Steven D'Aprano
Guest
Posts: n/a

 09-26-2010
On Sat, 25 Sep 2010 22:08:54 -0700, John Nagle wrote:

> On 9/25/2010 4:45 AM, Thomas Jollans wrote:
>> On Saturday 25 September 2010, it occurred to Yingjie Lan to exclaim:
>>> Hi,
>>>
>>> I noticed that in python3k, multiplying a sequence by a negative
>>> integer is the same as multiplying it by 0, and the result is an empty
>>> sequence. It seems to me that there is a more meaningful symantics.

>
> The concept that the multiply operator should be overloaded to
> do something l33t on sequences was a Python design mistake.

Repetition is a simple, useful operation, and describing it pejoratively
as "l33t" is just silly.

> It leads to semantics like this:
>
> x = 10
> y = "10"
>
> x*2
> 20
> y*2
> '1010'
> int(y*2)
> 1010
> int(y)*2
> 20

Yes. Where's the problem?

Are you also concerned that int(max("230", "9")) returns 9 but max(int
("230"), int("9")) returns 230?

It's obvious that there is no such invariant that int(f(y)) must equal
f(int(y)) for arbitrary functions f, so why do you think that the failure
of this invariant to hold for the * operator matters in the least?

> That's awful. It's the kind of thing a novice C++
> programmer would write shortly after they discovered operator

So you say.

> Now, ask yourself, when you get a numeric value back from
> a database, is it an integer or a string? How about the CSV module?
> Are you sure?

If you're not sure what sort of data you're getting, you've got no
business blindly applying functions in some arbitrary order to it in the
hope that the result will be sensible. If the data is meant to be an
integer, and you might get a string from the database, then convert it to
an integer as soon as possible. I'm surprised that you think that you
should be able to apply arbitrary mathematical operations to strings
*before* turning them into an int and still get sensible results. That
boggles my mind.

--
Steven

Paul Rubin
Guest
Posts: n/a

 09-26-2010
Steven D'Aprano <(E-Mail Removed)> writes:
> I'm surprised that you think that you should be able to apply
> arbitrary mathematical operations to strings *before* turning them
> into an int and still get sensible results. That boggles my mind.

I think the idea is you should not be able to do mathematical operations
on strings, and if you try to do one, Python should raise an exception,
rather than using hokey analogies to guess at what you were trying to
do. If you want to replicate a sequence, introduce a function with a

Steven D'Aprano
Guest
Posts: n/a

 09-26-2010
On Sat, 25 Sep 2010 23:46:57 -0700, Paul Rubin wrote:

> Steven D'Aprano <(E-Mail Removed)> writes:
>> I'm surprised that you think that you should be able to apply arbitrary
>> mathematical operations to strings *before* turning them into an int
>> and still get sensible results. That boggles my mind.

>
> I think the idea is you should not be able to do mathematical operations
> on strings, and if you try to do one, Python should raise an exception,
> rather than using hokey analogies to guess at what you were trying to
> do. If you want to replicate a sequence, introduce a function with a

But * isn't a mathematical operation on sequences. It's a repetition
operator. And repetition isn't a "hokey analogy" of multiplication --
you've got it completely backwards. Multiplication is a generalisation of
repetition, and sequence repetition is more fundamental than mathematical
multiplication. Long before anyone said 2.3 * 5.7 people were able to
talk about "two bags of wheat, and another two bags of wheat, and repeat
six more times". That's how multiplication was invented -- from repeated
addition. Think of writing numbers as tallies, before the invention of
arabic numerals. III groups of II would be IIIIII.

The semantics of list.index() and str.index() are not quite the same
(string indexing finds substrings, while list indexing does not find
sublists). So what? We have classes and modules and namespaces so that
you don't need every function to have a unique. It's okay that the index
method behaves slightly differently when operating on strings and lists,
and it's okay for the * operator to behave slightly differently too.

It is true that there's a certain class of coders who apparently like
line-noise, and so they overload operators to do arbitrary things with no

# sort the sequence by length
my_seq ** len

But this is bad because there's no convention for exponentiation being
related to sorting, and so the code is obscure and unintuitive. Without
the comments, you would have no idea what the line did. But that's just
the same as doing this:

def raise_to_power(alist, key):
alist.sort(key=key)
return alist

This is dumb whether you overload the ** operator or write a function
called "raise_to_power".

There's nothing obscure or unintuitive about "spam"*3 = "spamspamspam",
and the fact that it doesn't do the same thing as int("spam")*3 is a
foolish argument.

--
Steven

Paul Rubin
Guest
Posts: n/a

 09-26-2010
Steven D'Aprano <(E-Mail Removed)> writes:
> There's nothing obscure or unintuitive about "spam"*3 = "spamspamspam",

Why would it not be ["spam","spam","spam"] or even "ssspppaaammm"?
Should "spam"*2.5 be "spamspamsp"?
Should "spam"-"a" be "spm"? What about "spamspam"-"a"?
And what about "spam"/2? "sp" be an ok first guess, but "sa" might
make more sense (it means (1,2,3,...)/2 would be (1,3,5...)).

I say it's all hokey from the get-go .

Seebs
Guest
Posts: n/a

 09-26-2010
On 2010-09-26, Steven D'Aprano <(E-Mail Removed)> wrote:
> On Sat, 25 Sep 2010 23:46:57 -0700, Paul Rubin wrote:
>> I think the idea is you should not be able to do mathematical operations
>> on strings, and if you try to do one, Python should raise an exception,
>> rather than using hokey analogies to guess at what you were trying to
>> do. If you want to replicate a sequence, introduce a function with a

> But * isn't a mathematical operation on sequences. It's a repetition
> operator.

I think Paul's point is that "*" is in general a mathematical operation,
and discovering that for some types of objects, it's been given a
fundamentally unrelated meaning can certainly be a bit of a surprise.

I actually sort of like that perl has a separate repetition operator.
And really, when you find yourself saying something nice about perl's
syntax or punctuation, that's always scary territory to be in.

> There's nothing obscure or unintuitive about "spam"*3 = "spamspamspam",
> and the fact that it doesn't do the same thing as int("spam")*3 is a
> foolish argument.

The languages in which it's surprising are mostly things like perl, where
there's a certain amount of implicit coercion going on, so it's ambiguous
whether "3"*3 ought to mean "333" or 9. (Except it isn't, because repetition
is a separate operator, written "3" x 3 rather than "3" * 3...) People
coming from that kind of background may be expecting * to stay an
arithmetic operator, rather than having it change form when applied to
non-arithmetic objects.

I'm not sure either way. I think on the whole, I like the notion of
a repetition operator which is distinct from multiplication, but I also
definitely prefer the lack of implicit coercion, and without implicit
coercion, there's a lot less underlying ambiguity to worry about.

From the top, I guess my analysis is:

* It seems clear that, given two sequences x and y, "x + y" ought to
be the concatenation of these sequences.
* Thus, "x + x" should be x concatenated to itself.
* Idiomatically, it is not unreasonable to assert that "x * 2" and "x + x"
could be the same value.
* It thus makes some sense for <sequence> * <number> to indicate
repetition of the sequence.
* Since a string is a kind of a sequence, it also makes sense for
<string> * <number> to indicate repetition.

So I think I'm pretty much convinced that Python's behavior makes sense,
but it makes sense only because I'm going into this expecting operators
to be defined by types, so I don't expect every '*' to mean 'multiplication'.

Helps, perhaps, that I got exposed to group theory early enough to be used
to redefining + and * to be any two operations which have interesting
properties*.

-s[*] What's purple and commutes? An abelian grape.
--
Copyright 2010, all wrongs reversed. Peter Seebach / http://www.velocityreviews.com/forums/(E-Mail Removed)
http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
I am not speaking for my employer, although they do rent some of my opinions.

Paul Rubin
Guest
Posts: n/a

 09-26-2010
Seebs <(E-Mail Removed)> writes:
> * It seems clear that, given two sequences x and y, "x + y" ought to
> be the concatenation of these sequences.
>...
> Helps, perhaps, that I got exposed to group theory early enough to be used
> to redefining + and * to be any two operations which have interesting
> properties*.

But groups have only one of those operators, and when it's written as +
that usually means the group is commutative. So you wouldn't want + to
denote sequence concatenation. If + and * are both present, that sounds
like a ring, in which case you'd want "foo"*"bar" to work. It actually
seems to me that exponentiation makes more sense than multiplication
for turning "a" into "aaa". We think of aaa as what results from
writing "a" with "3" in the superscript position.

Grant Edwards
Guest
Posts: n/a

 09-26-2010
On 2010-09-26, Paul Rubin <(E-Mail Removed)> wrote:
> Steven D'Aprano <(E-Mail Removed)> writes:
>> There's nothing obscure or unintuitive about "spam"*3 = "spamspamspam",

> Why would it not be ["spam","spam","spam"] or even "ssspppaaammm"?

Because

3 * "spam" == "spam" + "spam" + "spam"

Just like

3 * 6 = 6 + 6 + 6

So now I suppose "+" for string concatenation is a bad thing.

--
Grant