On 17/03/2022 05.21, Stephen J. Turnbull wrote:
MRAB writes:
I'm wondering whether an alterative could be a function for splicing sequences such as lists and tuples which would avoid the need to create and then destroy intermediate sequences:
splice(alist, i, 1 + 1, [value])
Does this make sense for lists? I don't see how you beat
newlist = alist[:] newlist[index_or_slice] = value_or_sequence_respectively
(instead of newlist = alist[:i] + [value] + alist[i+1:], which involves creating 4 lists instead of 1). I wonder if it might be reasonable to peephole optimize
Recently (extra-patiently) explained to A.N.Other how indexes/indices and slices can be used on the LHS of an expression. So, I was really hoping that 'splicing' would make sense over the traditional break-and-build pattern. Sadly (for my hopes), from an execution-time perspective, there's nothing between them:-
import timeit as tm l = [ 0 , 1, 2, 3, 4, 5, 6, 7, 8, 9, 'ten', 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 ]
joiner = """\ ... def join_list( existing_list ): ... return existing_list[ :10 ] + [ 10 ] + existing_list[ 10+1: ] ... """ tm.timeit( stmt=joiner, number=100_000_000 ) 6.822223100927658
splicer = """\ ... def splice_list( existing_list ): ... new_list = existing_list[ : ] ... new_list[ 10 ] = 10 ... return new_list ... """ tm.timeit( stmt=splicer, number=100_000_000 ) 6.87068138003815
NB yes, outside of the simplified timing-environment, we'd want the index (10) and the value (int( 10 )) to be parameters! Both 'joiner' and 'splicer' require twice len( l ) of storage-space. Most of us will comfortably write 'joiner' in-line (cf the function above) - and when reading later, will recognise the construction for what it is. Some might prefer that the two lines of splice_list() be 'chunked' into a function, but given that splicing is built-in there's no reason why they couldn't be expressed in-line - other than (perhaps) a comparative lack of familiarity in reading/interpreting the two lines for what they're doing! Assuming we're prepared to use an utility-splicer (but not extend the list class directly), if we don't need to create a second list or must ration storage-space, the mutable-list could be directly, um, mutated:-
splice_in_place = """\ ... def splice_list(): ... the_list[ 10 ] = 10 ... """ the_list = l[ : ] tm.timeit( stmt=splice_in_place, number=100_000_000 ) 6.726862345007248
but please be prepared for code-review criticism! Once-again, there's no significant speed-advantage. So, ... If we're still talking about tuples, then the experiments could be repeated with appropriate list() and tuple() coercions. -- Regards, =dn