سؤال

مؤخرا كتبت قليلا من لوا رمز شيئا مثل:

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 حالات:

  1. الجدول مرجع دائري
  2. مفاتيح التي هي أيضا الجداول
  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. تنفيذ لوا محدودة جدا مع عدم وجود مكتبة مركزية من الوظائف التي يمكن الرجوع إليها. تحتاج كل وظيفة ليعلن في القانون وذلك للحفاظ رمز للخدمة، لذلك أحد الحلول مثل هذا الخط مواتية.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top