Kaius [Lexaloffle Blog Feed]https://www.lexaloffle.com/bbs/?uid=65920 Functions for copying objects without linkage <p>One nice thing about objects is that objects set with the <code>=</code> operator or as arguments are all 'linked' meaning, among other things, you can provide them as arguments for a function call and that function will be able to edit the local copy of that object and have it affect the original one too. But sometimes, you <em>don't</em> want that. Sometimes you want to copy an object, make changes to it, and then discard the new copy without affecting the old one. That's why I've created a little helper function for this circumstance. Actually, I've created several. Here's the fully-featured 'default' function:</p> <div> <div class=scrollable_with_touch style="width:100%; max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>function cpy_obj(obj,recursion) if type(obj)==&quot;table&quot; then local return_value={} for k,v in pairs(obj) do if recursion then v=cpy_obj(v,true) end return_value[k]=v end return return_value else return obj end end</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p>Usage: <code>copy= cpy_obj(𝘰𝘣𝘫𝘦𝘤𝘵)</code> or <code>function_call( cpy_obj(𝘰𝘣𝘫𝘦𝘤𝘵) )</code></p> <p><code>return_value</code> is the new object being created. Note that since we are creating it a piece at a time, it has <em>no</em> affiliation with the previous object. If you give it something that is not an object, it will just give you the input variable back. <code>recursion</code> is a boolean that asks if we want to make nested objects be decoupled as well. You <strong>usually</strong> want this if you have nested objects, but keep in mind that this comes with a performance cost, and although it's rather small, you might not want that if you're pushing up against the limits of PICO-8.</p> <h2>Variants</h2> <p>Every project is different, and with that comes different requirements. One person will be working on a game jam that has no risk of reaching the token limit, and another will be working on a 3D engine that needs to be as compact as possible. As such, I have a few different variants that may cater to your project's particular demands...</p> <h3>Forced recursion</h3> <p><div><div><input type="button" value=" Show " onClick="if (this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display != '') { this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display = ''; this.innerText = ''; this.value = ' Hide '; } else { this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display = 'none'; this.innerText = ''; this.value = ' Show '; }"></div><div><div style="display: none;"></p> <div> <div class=scrollable_with_touch style="width:100%; max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>function cpy_obj(obj) if type(obj)==&quot;table&quot; then local return_value={} for k,v in pairs(obj) do v=cpy_obj(v) return_value[k]=v end return return_value else return obj end end</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p></div></div></div><br /> This one shaves off five tokens (plus one every time you need recursion!), but comes at the downside of a forced performance cost. Still, this one is probably closer to being the more useful one than the default one due to the token savings.</p> <h3>No recursion</h3> <p><div><div><input type="button" value=" Show " onClick="if (this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display != '') { this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display = ''; this.innerText = ''; this.value = ' Hide '; } else { this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display = 'none'; this.innerText = ''; this.value = ' Show '; }"></div><div><div style="display: none;"></p> <div> <div class=scrollable_with_touch style="width:100%; max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>function cpy_obj(obj) if type(obj)==&quot;table&quot; then local return_value={} for k,v in pairs(obj) do return_value[k]=v end return return_value else return obj end end</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p></div></div></div><br /> This one saves five <strong>more</strong> tokens than the previous one, but <em>lacks</em> recursion entirely. Of course, you won't always (and usually don't) need recursion, but if you do then this function won't cut it for you.</p> <h3>No non-object return values</h3> <p><div><div><input type="button" value=" Show " onClick="if (this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display != '') { this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display = ''; this.innerText = ''; this.value = ' Hide '; } else { this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display = 'none'; this.innerText = ''; this.value = ' Show '; }"></div><div><div style="display: none;"></p> <div> <div class=scrollable_with_touch style="width:100%; max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>function cpy_obj(obj) local return_value={} for k,v in pairs(obj) do return_value[k]=v end return return_value end</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p></div></div></div><br /> This one save <strong>TEN</strong> more tokens than even the last one, but non-object inputs will result in a crash as the API function <code>pairs()</code> won't know what to do.</p> <h3>Ultra-cheap snippet</h3> <p><div><div><input type="button" value=" Show " onClick="if (this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display != '') { this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display = ''; this.innerText = ''; this.value = ' Hide '; } else { this.parentNode.parentNode.getElementsByTagName('div')[1].getElementsByTagName('div')[0].style.display = 'none'; this.innerText = ''; this.value = ' Show '; }"></div><div><div style="display: none;"></p> <div> <div class=scrollable_with_touch style="width:100%; max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>𝘯𝘦𝘸_𝘰𝘣𝘫={} for k,v in pairs(𝘰𝘭𝘥_𝘰𝘣𝘫) do 𝘯𝘦𝘸_𝘰𝘣𝘫[k]=v end</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> <p></div></div></div><br /> This one is cheaper than all of the others at just thirteen tokens, but must be copy-pasted everywhere you intend to use it meaning it only saves tokens if you only use it in one or two spots (which very well may be the case). Whatever you do, be sure to replace <code>𝘰𝘭𝘥_𝘰𝘣𝘫</code> and <code>𝘯𝘦𝘸_𝘰𝘣𝘫</code> with your object names.</p> <h2>Comparisons</h2> <p>Lastly, here's a list of the token costs of each function, assuming all arguments are one variable:</p> <div> <div class=scrollable_with_touch style="width:100%; max-width:800px; overflow:auto; margin-bottom:12px"> <table style="width:100%" cellspacing=0 cellpadding=0> <tr><td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> <td background=/gfx/code_bg0.png> <div style="font-family : courier; color: #000000; display:absolute; padding-left:10px; padding-top:4px; padding-bottom:4px; "> <pre>Default : 42 to define , 3 or 4 per call Forced recursion : 37 to define , 3 per call No recursion: : 32 to define , 3 per call No non-object return values : 22 to define , 3 per call Ultra-cheap snippet : no definition , 13 per call</pre></div></td> <td background=/gfx/code_bg1.png width=16><div style="width:16px;display:block"></div></td> </tr></table></div></div> https://www.lexaloffle.com/bbs/?tid=50606 https://www.lexaloffle.com/bbs/?tid=50606 Sun, 11 Dec 2022 07:27:31 UTC