كيف يمكنك نسخ لوا الجدول حسب القيمة ؟
سؤال
مؤخرا كتبت قليلا من لوا رمز شيئا مثل:
local a = {}
for i = 1, n do
local copy = a
-- alter the values in the copy
end
ومن الواضح أن هذا لم يكن ما أردت القيام به منذ المتغيرات عقد المراجع مجهول الجدول لا قيم الجدول أنفسهم في لوا.هذا هو بوضوح في البرمجة Lua, ولكن قد نسيت حول هذا الموضوع.
لذا فإن السؤال هو ماذا يجب أن أكتب بدلا من copy = a
للحصول على نسخة من القيم في a
?
المحلول
للعب قليلا للقراءة رمز غولف ، وهنا نسخة قصيرة أن يعالج المعيار صعبة الحالات:
- الجداول مفاتيح ،
- الحفاظ على metatables ،
- عودي الجداول.
يمكننا أن نفعل هذا في 7 خطوط:
function copy(obj, seen)
if type(obj) ~= 'table' then return obj end
if seen and seen[obj] then return seen[obj] end
local s = seen or {}
local res = setmetatable({}, getmetatable(obj))
s[obj] = res
for k, v in pairs(obj) do res[copy(k, s)] = copy(v, s) end
return res
end
هناك متابعة الكتابة قصيرة من لوا العميقة نسخ العمليات في هذا جوهر.
آخر من المراجع المفيدة هذا لوا المستخدمين صفحة ويكي, والتي تشمل على سبيل المثال على كيفية تجنب __pairs
metamethod.
نصائح أخرى
ونسخة الجدول لديها العديد من التعاريف المحتملة. ذلك يعتمد على ما إذا كنت تريد نسخة بسيطة أو عميقة، ما إذا كنت تريد نسخ، حصة أو تجاهل metatables، وما إلى ذلك لا يوجد تنفيذ واحد يمكن أن يرضي الجميع.
ونهج واحد هو ببساطة إنشاء جدول جديد وتكرار جميع أزواج مفتاح / قيمة:
function table.shallow_copy(t)
local t2 = {}
for k,v in pairs(t) do
t2[k] = v
end
return t2
end
copy = table.shallow_copy(a)
ملحوظة التي يجب استخدامها pairs
بدلا من ipairs
، منذ ipairs
أعاد فقط على مجموعة فرعية من مفاتيح الجدول (أي. متتالية مفاتيح صحيح إيجابية ابتداء من الساعة واحدة في زيادة الطلب).
وفقط لتوضيح هذه النقطة، كما يدفع لي table.copy
الشخصية الانتباه إلى metatables:
function table.copy(t)
local u = { }
for k, v in pairs(t) do u[k] = v end
return setmetatable(u, getmetatable(t))
end
وهناك وظيفة نسخة بما فيه الكفاية وافق على نطاق واسع على أن يسمى "المعيار".
النسخة الكاملة من نسخة عميق ، والتعامل مع كل 3 حالات:
- الجدول مرجع دائري
- مفاتيح التي هي أيضا الجداول
- Metatable
العامة الإصدار:
local function deepcopy(o, seen)
seen = seen or {}
if o == nil then return nil end
if seen[o] then return seen[o] end
local no
if type(o) == 'table' then
no = {}
seen[o] = no
for k, v in next, o, nil do
no[deepcopy(k, seen)] = deepcopy(v, seen)
end
setmetatable(no, deepcopy(getmetatable(o), seen))
else -- number, string, boolean, etc
no = o
end
return no
end
أو الجدول الإصدار:
function table.deepcopy(o, seen)
seen = seen or {}
if o == nil then return nil end
if seen[o] then return seen[o] end
local no = {}
seen[o] = no
setmetatable(no, deepcopy(getmetatable(o), seen))
for k, v in next, o, nil do
k = (type(k) == 'table') and k:deepcopy(seen) or k
v = (type(v) == 'table') and v:deepcopy(seen) or v
no[k] = v
end
return no
end
استنادا إلى lua-users.org/wiki/CopyTable's ، آلان ييتس وظائف'.
وهناك العميق اختياريا، الرسم البياني العام، الإصدار العودية:
function table.copy(t, deep, seen)
seen = seen or {}
if t == nil then return nil end
if seen[t] then return seen[t] end
local nt = {}
for k, v in pairs(t) do
if deep and type(v) == 'table' then
nt[k] = table.copy(v, deep, seen)
else
nt[k] = v
end
end
setmetatable(nt, table.copy(getmetatable(t), deep, seen))
seen[t] = nt
return nt
end
وربما ينبغي أن يكون نسخة metatable اختياري أيضا؟
إليك ما فعلته في الواقع:
for j,x in ipairs(a) do copy[j] = x end
دوب يذكر ، إذا مفاتيح الجدول الخاص بك ليست زيادة monotonically بدقة، ينبغي pairs
لا ipairs
.
وكما أنني وجدت deepcopy
ظيفة التي هي أكثر قوة:
function deepcopy(orig)
local orig_type = type(orig)
local copy
if orig_type == 'table' then
copy = {}
for orig_key, orig_value in next, orig, nil do
copy[deepcopy(orig_key)] = deepcopy(orig_value)
end
setmetatable(copy, deepcopy(getmetatable(orig)))
else -- number, string, boolean, etc
copy = orig
end
return copy
end
وكان يعالج الجداول وmetatables التي تطلق على نفسها بشكل متكرر ( وهو مكافأة تلقاء نفسها ). واحدة من بت ذكية هو أنك يمكن أن تمر عليه أي قيمة (سواء كان جدول أو لا) وسوف يتم نسخها بشكل صحيح. ومع ذلك، فإن تكلفة هو أنه يحتمل أن تجاوز المكدس. لذلك وحتى أكثر قوة (غير متكررة) تعمل قد تكون هناك حاجة.
ولكن هذا مبالغة لحالة بسيطة جدا من الرغبة في نسخ مجموعة إلى متغير آخر.
وو(للأسف وثقت خفيفة) stdlib المشروع عدد من الملحقات قيمة للعديد من المكتبات يتم شحنها مع توزيع لوا القياسية. بينهم العديد من الاختلافات حول موضوع نسخ الجدول والدمج.
وأيضا يتم تضمين هذه المكتبة في لوا لتوزيع Windows ، وربما ينبغي أن تكون جزءا من أي الأدوات خطير المستخدم لوا و.
وشيء واحد للتأكد من عند تنفيذ مثل هذه الامور باليد هو التعامل الصحيح مع metatables. لتطبيقات بسيطة الجدول كما هو وهيكل وربما كنت لا تملك أي metatables، وحلقة بسيطة باستخدام pairs()
هو الجواب مقبولة. ولكن إذا تم استخدام الجدول مثل شجرة، أو تحتوي على مراجع دائرية، أو لديه metatables، ثم الأمور أكثر تعقيدا.
لا ننسى أن مهام المراجع ، لذلك إذا أردت تماما 'نسخ' كل القيم التي سوف تحتاج إلى الحصول على وظائف منفصلة أيضا ؛ غير أن الطريقة الوحيدة التي أعرف أن نسخة وظيفة هو استخدام loadstring(string.dump(func))
, والتي وفقا لوا دليل مرجعي لا تعمل الوظائف مع upvalues.
do
local function table_copy (tbl)
local new_tbl = {}
for key,value in pairs(tbl) do
local value_type = type(value)
local new_value
if value_type == "function" then
new_value = loadstring(string.dump(value))
-- Problems may occur if the function has upvalues.
elseif value_type == "table" then
new_value = table_copy(value)
else
new_value = value
end
new_tbl[key] = new_value
end
return new_tbl
end
table.copy = table_copy
end
تحذير:ملحوظة الحل غير صحيحة!
عندما يحتوي الجدول على الجداول إشارات إلى تلك الجداول سوف لا يزال يتم استخدامها بدلا من ذلك.لقد تم البحث ساعتين بسبب الخطأ الذي كنت صنع, في حين كان بسبب استخدام رمز أعلاه.
لذلك تحتاج إلى التحقق من ما إذا كانت قيمة هو الجدول أم لا.إذا كان يجب أن تتصل الجدول.نسخ متكرر!
هذا هو الجدول الصحيح.وظيفة النسخ:
function table.copy(t)
local t2 = {};
for k,v in pairs(t) do
if type(v) == "table" then
t2[k] = table.copy(v);
else
t2[k] = v;
end
end
return t2;
end
ملاحظة:هذا قد يكون أيضا غير مكتملة عندما يحتوي الجدول على وظائف أو غيرها من أنواع خاصة ، ولكن هذا هو الممكن شيء معظمنا لا تحتاج إليها.رمز أعلاه هي قابلة للتكيف بسهولة لأولئك الذين في حاجة إليها.
وهذا امر جيد كما ستحصل على الجداول الأساسية. استخدام شيء من هذا القبيل deepcopy إذا كنت بحاجة إلى نسخ الجداول مع metatables.
وأعتقد أن السبب لوا لا يكون 'table.copy ()' في مكتباتها القياسية لأن المهمة ليست دقيقة لتحديد. كما هو موضح هنا بالفعل، يمكن للمرء إما جعل نسخة "مستوى واحد عميق" (التي فعلت)، وdeepcopy مع أو بدون رعاية من المراجع المكررة المحتملة. وبعد ذلك هناك metatables.
وشخصيا، ما زلت أحب لهم لتقديم وظيفة المضمنة. إلا إذا كان الناس لا يكون من دواعي سرور مع معاني الكلمات، فإنها بحاجة للذهاب القيام بذلك بأنفسهم. ليس كثيرا جدا، على الرغم من واحد في الواقع ضرورة نسخة من جانب القيمة.
في معظم الحالات عندما كنت في حاجة لنسخ الجدول، أردت أن يكون لديك نسخة التي لا تشارك أي شيء مع الأصل، بحيث أي تعديل في الجدول الأصلي ليس له أي تأثير على نسخة (والعكس بالعكس ).
جميع قصاصات التي ثبت حتى تفشل حتى الآن في إنشاء نسخة للجدول الذي قد تقاسمت مفاتيح أو مفاتيح مع الجداول مثل تلك ذاهبون إلى أن تترك لافتا إلى الجدول الأصلي. فمن السهل أن نرى إذا حاولت نسخ جدول تم إنشاؤه على النحو التالي: a = {}; a[a] = a
. deepcopy وظيفة المشار إليه من قبل جون يعتني ذلك، لذلك إذا كنت بحاجة إلى إنشاء الحقيقية / نسخة كاملة ، ينبغي أن تستخدم deepcopy
.
استخدم penlight مكتبة هنا: https://stevedonovan.github.io/Penlight/api/libraries /pl.tablex.html#deepcopy
local pl = require 'pl.import_into'()
local newTable = pl.tablex.deepcopy(oldTable)
وهذا قد يكون أبسط طريقة:
local data = {DIN1 = "Input(z)", DIN2 = "Input(y)", AINA1 = "Input(x)"}
function table.copy(mytable) --mytable = the table you need to copy
newtable = {}
for k,v in pairs(mytable) do
newtable[k] = v
end
return newtable
end
new_table = table.copy(data) --copys the table "data"
في وضعي، عندما المعلومات الواردة في الجدول هي الوحيدة البيانات والجداول الأخرى (باستثناء وظائف، ...)، هو السطر التالي من التعليمات البرمجية الحل الفوز:
local copyOfTable = json.decode( json.encode( sourceTable ) )
وأنا أكتب كود لوا لبعض أتمتة المنزل على Fibaro هوم سنتر 2. تنفيذ لوا محدودة جدا مع عدم وجود مكتبة مركزية من الوظائف التي يمكن الرجوع إليها. تحتاج كل وظيفة ليعلن في القانون وذلك للحفاظ رمز للخدمة، لذلك أحد الحلول مثل هذا الخط مواتية.