Message Boards Message Boards

0
|
10852 Views
|
20 Replies
|
14 Total Likes
View groups...
Share
Share this post:

Modify a list-element in place?

Anonymous User
Anonymous User
Posted 7 years ago

This works:

In[1]:= testList = {1, 2, 3};

In[2]:= testList[[2]] += 7;

In[3]:= testList

Out[3]= {1, 9, 3}

I want to generalize it like this (this is not what Mathematica actually does, but it shows the results I want to accomplish somehow):

In[4]:= add7To2ndElement[list_] := list[[2]] += 7

In[5]:= Module[
 {list = {1, 2, 3}},
 add7To2ndElement[list];
 list
 ]

Out[5]= {1, 9, 3}

This is what Mathematica actually does, instead of what I'm trying to accomplish:

In[4]:= add7To2ndElement[list_] := list[[2]] += 7

In[5]:= Module[
 {list = {1, 2, 3}},
 add7To2ndElement[list];
 list
 ]

During evaluation of In[5]:= Set::setps: {1,2,3} in the part assignment is not a symbol.

Out[5]= {1, 2, 3}

How can I rewrite my code so that it alters the list-element in place? The actual list I'm working on is large, and I have to modify its elements a large number of times, so for efficiency's sake, I need to avoid methods that make a fully copy of the list each time I modify an element.

POSTED BY: Anonymous User
20 Replies
Posted 7 years ago

Joe, could you use the very simple approach shown below?

In[1]:= array = Table[i, {i, 960000}];

In[2]:= Take[array, {425, 429}]

Out[2]= {425, 426, 427, 428, 429}

In[3]:= array[[427]] = x

Out[3]= x

In[4]:= Take[array, {425, 429}]

Out[4]= {425, 426, x, 428, 429}
POSTED BY: Hans Milton
Anonymous User
Anonymous User
Posted 7 years ago

Thanks, Hans. There may (or not) be a way to accomplish what I want in Mathematica, but it would have required me to saturate my code with "SetAttributes[*,HoldFirst"] statements---which makes source-code unwieldy---and it would have required me to rearrange all my existing function-definitions and function-calls so that large arrays were passed as the first parameter. And then later, if I wanted to pass a 2nd large array as a 2nd parameter, I would have had to make major adjustments to get it to Hold the 2nd parameter in addition to the 1st. That's inconsistent with my goal of working efficiently, so I'm not using Mathematica for this job anymore.

I'd continually be running into undocumented surprises like this. Surprises aren't necessary, as the language-specs for C and Common Lisp are thorough. So far Python has not surprised me or required me to run experiments in order to figure out how to use it.

And bugs that go unacknowledged by Wolfram: here. (No, I'm not going to email them about; that would be above my pay-grade.)

Mathematica will be the best tool when I'm doing high-level math, but I don't think it's the best tool for this low-level data-processing I'm working on currently.

POSTED BY: Anonymous User
Anonymous User
Anonymous User
Posted 7 years ago

In less time than it took me to figure out that render[] (in addition to AddX[]) needs a HoldFirst, I installed Python, learned Python, and finished my program in Python.

In[1]:= SetAttributes[addX, HoldFirst]

In[2]:= addX[monoBuffer_] := monoBuffer[[2]] += x

In[3]:= (*With[{monoBuffer=Range[5]},addX[monoBuffer];monoBuffer]*)

In[4]:= Module[{monoBuffer = Range[5]}, addX[monoBuffer]; monoBuffer]

Out[4]= {1, 2 + x, 3, 4, 5}

In[5]:= render[monoBuffer_] := addX[monoBuffer]

In[6]:= Module[{monoBuffer = Range[5]}, render[monoBuffer]]

During evaluation of In[6]:= Set::setps: {1,2,3,4,5} in the part assignment is not a symbol.

Out[6]= 2 + x

In[7]:= SetAttributes[render, HoldFirst]

In[8]:= Module[{monoBuffer = Range[5]}, render[monoBuffer]]

Out[8]= 2 + x

For symbolically calculating integrals, Mathematica is helpful; for solving equations, it's helpful if you lucky enough to phrase your question in a way Mathematica understands; but for digital-signal-processing, Mathematica is a hindrance. There are too many free alternatives that don't require you to run experiments, trial-and-error, and jump through hoops in order to simply pass an array by reference. These free languages---e.g., C, Common Lisp, Python---are thoroughly defined by their respective language-specs; Wolfram Language is vaguely described by its docs, and the rest is left to the user to figure out through trial-and-error or asking questions on forums.

POSTED BY: Anonymous User

I'm really not sure why you would introduce those Modules there, you don't need them. Please describe what you want to do, i'm fairly certain your problem can be stated in a much easier way. On the note of other languages: C, and Lisp are both, by default, call by value, one has to do work to pass by reference. Furthermore, Python is neither of them; it is 'call by object' if you will. In Mathematica this is done using the UnEvaluated or HoldFirst/HoldAll statements: (in C(++) using * and &, et cetera...)

Attributes[MyFunction,HoldFirst]
MyFunction[x_,y_,z_] := Module[{},
    ...
    ...
    your code here
    ...
    ...
]

Now one can call YourFunction...do all the operations on it, and have it all local. without copies. Mathematica can do all these low-order operations but I'd bet there is a higher-level function that can do your signal processing. If it simply adding numbers, it can e.g. be done all at once using vectors, rather than 1 by 1, or use function like Map, Apply, Array et cetera, they are vastly more efficient than doing it manually one-by-one and let's you easily include parallel programming...

POSTED BY: Sander Huisman
Anonymous User
Anonymous User
Posted 7 years ago

I'm really not sure why you would introduce those Modules there, you don't need them.

That is what I first assumed too, but if you replace "Module" with "With", as I had in my initial attempts, you will get additional errors. I found my way to "Module" only through trial-and-error, divide-and-conquer, and other time-consuming and frustrating debugging methods.

Why I need any scoping construct there at all is because my actual code is more like this:

Module[{monoBuffer = Range[5]},
 render[monoBuffer];
 <do various things to monoBuffer after it's been processed by render[]>]

C, and Lisp are both, by default, call by value, one has to do work to pass by reference.

That's not true of C-arrays or Lisp-lists (in Common Lisp). The only thing passed by value is a pointer to the head of the array/list. The body of the array or list is effectively passed by reference.

(setq testList (list 1 2 3 4))
(defun modifyList (list) (rplaca (nthcdr 2 list) 9))
(modifyList testList)
testList
(1 2 9 4)
POSTED BY: Anonymous User

For lists/arrays yes, but in general values no.

Anyhow, you still haven't told us what you want to do, I presume your end goal is not adding 7 to the second element. This thread seems kinda useless if it just you complaining about something that you don't fully understand because you have not studied it very well yet...

POSTED BY: Sander Huisman
Anonymous User
Anonymous User
Posted 7 years ago

you still haven't told us what you want to do

because certain details are irrelevant and not public information.

This thread seems kinda useless if it just you complaining about something that you don't fully understand because you have not studied it very well yet...

I'm explaining why I won't be investing further in Mathematica. As a fan, you have the luxury of digging your feet in, blaming everything on the users, and defending shortcomings in a program you apparently worship; but for the developers running a business and wondering why there will be one less customer returning, I'm offering helpful information.

I have studied what I'm doing well enough to have coded parts of the world's 1st commercial software synthesizer.

POSTED BY: Anonymous User

Hi Joe,

frustration of this kind is typical when the need of solving a specific problem is the sole motivation of learning WL. This compares to the situation when starting to learn a real language (e.g. French) not before actually having to translate some text. This is not working - and I have no idea why again and again people envision it could! WL is immensely powerful, but it needs some effort first. And besides: Before being able to write successfully efficient programs e.g. in C one needs a whole lot of experience as well!

Regards -- Henrik

POSTED BY: Henrik Schachner
Anonymous User
Anonymous User
Posted 7 years ago

When I want to integrate symbolically, I will use Mathematica, not any other language. Symbolic was manipulation was a primary reason for trying/buying Mathematica, and it's good for that. That was the original emphasis in 1987, if I understand correctly. Now that it's grown, though, and they have an entire chapter in the docs titled "Sound", they could enhance the design or at least the docs to make it easier to pass massive arrays around?

And yes, Python took me only a few hours to learn, but that's because I've already spent years doing C/C++.

POSTED BY: Anonymous User
Posted 7 years ago

Use Unevaluated in the function call:

In[1]:= add7To2ndElement[x_] := x[[2]] += 7

In[2]:= Module[
    {list = {1, 2, 3}},
    add7To2ndElement[Unevaluated@list];
    list
 ]

Out[2]= {1, 9, 3}
POSTED BY: Hans Milton

If you don't want to write Unevaluated every time, you can also set HoldFirst once:

SetAttributes[add7To2ndElement, HoldFirst]
add7To2ndElement[list_] := list[[2]] += 7

Module[{list = {1, 2, 3}}, add7To2ndElement[list]; list]
POSTED BY: Sander Huisman
Anonymous User
Anonymous User
Posted 7 years ago

Thank you both. I've tried both of your solutions here, and they both work here.

Even after studying the docs on evaluation and the Hold* and Unevaluated functions, I don't have a clear understanding of why my original attempt was erroneous. I figure my original attempt was causing this to happen:

In[1]:= {1, 2, 3}[[2]] += 7

During evaluation of In[1]:= Set::setps: {1,2,3} in the part assignment is not a symbol.

Out[1]= 9

I understand that in this example, Mathematica is attempting to do "2+=7", which is understandably an error; but is there a way that code could be rewritten to return {1,9,3}, without using any named variables?

POSTED BY: Anonymous User

Basically (roughly?) += (the function called AddTo) does the following:

x = x + dx

so in your example that translates to:

2 = 2 + 7

However you can't set '2' to something! it's not a symbol!

When you use set (=) the left hand side has to be a symbol; it can't be a number or string or ...

I'm not sure what you mean 'without using any named variables', do you mean that the function also excepts lists (not symbols):

add7To2ndElement[list_] := Module[{out = list}, out[[2]] += 7; out]
add7To2ndElement[{1, 2, 3, 4}]

Or do you mean it does not have any intermediate named variables:

add7To2ndElementAnonymous = ReplacePart[#, 2 -> #[[2]] + 7] &
add7To2ndElementAnonymous[{1, 2, 3, 4}]

Both return {1, 9, 3, 4}...

POSTED BY: Sander Huisman
Anonymous User
Anonymous User
Posted 7 years ago

Thanks for your patience as I try to learn this. I am able to manage l-values and r-values in C and Lisp, so I hope I can eventually comprehend Wolfram Language also. I'll try putting what I've learned here into practice.

The docs say, "Functions like ReplacePart take explicit lists and give you new lists." It's unclear to me whether that means they do a full copy of the list, but I'll run some experiments about that.

I'm working on a List of length 960,000, so I need to be careful not to cause any full-copies while I modify its elements.

POSTED BY: Anonymous User

Nearly all functions make copies of the inputs, modify it somehow, and return a new list, not explicitly overwriting your variables, exceptions are:

AppendTo
PrependTo
AssociateTo
KeyDropFrom
AddTo
SubtractFrom
TimesBy
DivideBy
(Pre)Increment
(Pre)Decrement

These will update the first argument (a symbol) and will also return something. Not creating copies. Note that they have a 'To' , 'From' or "By" et cetera at the end to distinguish them from:

Append
Prepend
KeyDrop
Add (Plus!)
Subtract
Times
Divide

which all create new lists.

POSTED BY: Sander Huisman
Anonymous User
Anonymous User
Posted 7 years ago

I hope I'm misunderstanding the limitation you're describing. I want to process 20 seconds or more of audio data sampled at 48,000 samples per second. This entails modifying each sample, and modifying it numerous times. It would be highly impractical if the full list were duplicated each time I adjusted a single sample. Octave is geared for processing large arrays like I want to do, but of course Octave omits thousands of Mathematica features which I don't want to leave.

POSTED BY: Anonymous User

That is why you have construct like HoldFirst, HoldAll, UnEvaluated et cetera, so you can pass it on 'by reference' (a la pointer) rather than 'by value'. The function I mentioned above demand all have the first argument 'HoldFirst', and they work pointer-like.

But please come with a concrete (yet simplified) examples, rather than these hypothetical ones that display your problem.

POSTED BY: Sander Huisman
Anonymous User
Anonymous User
Posted 7 years ago

I've tried HoldFirst (your first example in this thread) in the actual application I'm developing, and it appears on initial testing to solve my practical problem.

So at this time, my only remaining problem is that I don't understand the Wolfram Language as well as I'd like to. Here's a concrete example demonstrating a hole in my understanding. Alternative 1:

Function[buffer, (buffer[[2]] += x); buffer][Table[i, {i, 1000000}]]

That is an error. So, I try Alternative 2:

Function[buffer, (ReplacePart[buffer, 2 -> buffer[2] + x]); buffer][
 Table[i, {i, 1000000}]]

That causes no error, but because I've resorted to ReplacePart[], it duplicates the list and returns the unprocessed list {1,2,3,...}.

What is the smallest modification I could make so that it returned {1,2+x,3,...}---and without duplicating the entire long list? Is that possible?

POSTED BY: Anonymous User

Function does not have HoldFirst, it will pass on the values 'by value'. So buffer[[2]] = will be some number on the left hand side, spawning the error. ReplacePart does not change 'buffer'. It will create a copy of buffer and return the modified version of it.

When you return something it will automatically be a copy (I'm pretty sure about this), But that is not necessary:

SetAttributes[f, HoldFirst]
f[x_] := (x[[2]] += 7;)

testing:

a = Range[5]
f[a]
a

{1, 2, 3, 4, 5}
{1, 9, 3, 4, 5}

applying f to a will modify a, the function returns Null, but a is modified.

O btw, I think Octave is also mostly (like Matlab) 'pass by value'; making copies everywhere.

POSTED BY: Sander Huisman
Anonymous User
Anonymous User
Posted 7 years ago

Thanks for the clear demonstrations.

That is interesting about Octave; I have barely used it, and may have assumed wrongly about it. I should have used C/C++ as my example, because that's a language I'm actually familiar with. In C/C++, passing arrays by reference is the norm, and you have to go out of your way to pass them by value; that's the paradigm I'm accustomed to.for digital-audio processing.

POSTED BY: Anonymous User
Reply to this discussion
Community posts can be styled and formatted using the Markdown syntax.
Reply Preview
Attachments
Remove
or Discard

Group Abstract Group Abstract