يعيد مجموعة كاملة من الروتين الفرعي بيرل غير فعال؟

StackOverflow https://stackoverflow.com/questions/546175

سؤال

غالبا ما يكون لدي روتين فرعي في بيرل يملأ صفيف مع بعض المعلومات. منذ أن كنت معتادا أيضا على القرصنة في C ++، أجد نفسي غالبا ما تفعل ذلك مثل هذا في بيرل، باستخدام المراجع:

my @array;
getInfo(\@array);

sub getInfo {
   my ($arrayRef) = @_;
   push @$arrayRef, "obama";
   # ...
}

بدلا من الإصدار الأكثر وضوحا:

my @array = getInfo();

sub getInfo {
   my @array;
   push @array, "obama";
   # ...
   return @array;
}

السبب، بالطبع، هو أنني لا أريد إنشاء الصفيف محليا في الروتين الفرعي ثم نسخها عند العودة.

هل هذا صحيح؟ أو لا يحسن بيرل بعيدا على أي حال؟

هل كانت مفيدة؟

المحلول

ماذا عن إعادة مرجع صفيف في المقام الأول؟

sub getInfo {
  my $array_ref = [];
  push @$array_ref, 'foo';
  # ...
  return $array_ref;
}

my $a_ref = getInfo();
# or if you want the array expanded
my @array = @{getInfo()};

تحرير وفقا لتعليق دهسمان:

من الممكن أيضا استخدام مجموعة عادية في الوظيفة وإرجاع مرجع إليها.

sub getInfo {
  my @array;
  push @array, 'foo';
  # ...
  return \@array;
}      

نصائح أخرى

مراجع المرور أكثر كفاءة، ولكن الفرق ليس كبيرا كما هو الحال في C ++. تقدر الحجة نفسها (هذه الوسيلة: يتم تمرير القيم الموجودة في الصفيف) دائما عن طريق المرجع على أي حال (يتم نسخ القيم المرتجعة رغم ذلك).

السؤال هو: هل يهم؟ معظم الوقت، لا. إذا كنت تعود 5 عناصر، لا تهتم به. إذا كنت تعود / تمر 100000 عنصر، استخدم المراجع. فقط تحسينه إذا كان عنق الزجاجة.

إذا نظرت إلى مثالك والتفكير في ما تريد القيام به أنا معتاد على كتابة ذلك بهذه الطريقة:

sub getInfo {
  my @array;
  push @array, 'obama';
  # ...
  return \@array;
}

يبدو لي كما الإصدار المباشر عندما أحتاج إلى إرجاع كمية كبيرة من البيانات. لا تحتاج إلى تخصيص صفيف خارج sub كما كتبت في مقتطفك الأول my افعلها لك. على أي حال يجب ألا تفعل التحسين المبكر ليون تيممنمان يقترح.

للإجابة على الترميم النهائي، لا، بيرل لا يتحسن هذا بعيدا. لا يمكن، حقا، لأن إعادة صفيف وإعادة العددية مختلفة بشكل أساسي.

إذا كنت تتعامل مع كميات كبيرة من البيانات أو إذا كان الأداء مصدر قلق كبير، فإن عاداتك C سوف تخدمك جيدا ومراجع المراجع إلى هياكل البيانات بدلا من الهياكل التي لن تحتاج إلى نسخها. ولكن، كما أشار Leon Timmermans، فإن الغالبية العظمى من الوقت، أنت تتعامل مع كميات أصغر من البيانات والأداء ليست مشكلة كبيرة، لذلك تفعل ذلك بأي طريقة تبدو أكثر قابلية للقراءة.

هذه هي الطريقة التي سأعود فيها عادة صفيف.

sub getInfo {
  my @array;
  push @array, 'foo';
  # ...
  return @array if wantarray;
  return \@array;
}

بهذه الطريقة سوف تعمل بالطريقة التي تريدها أو في سياقات العددية أو قائمة.

my $array = getInfo;
my @array = getInfo;

$array->[0] == $array[0];

# same length
@$array == @array;

لن أحاول تحسينه إلا إذا كنت تعرف أنه جزء بطيء من التعليمات البرمجية الخاصة بك. حتى بعد ذلك، أود استخدام المعايير لمعرفة الروتين الفرعي أسرع في الواقع.

هناك اعتبارين. الوحيد الواضح هو ما حجم صفيفك؟ إذا كان الأمر أقل من عشرة عناصر أقل، فإن الحجم ليس عاملا (ما لم يكن التحسين الصغير لبعض الوظائف بسرعة، ولكن عليك القيام ببعض تنميط الذاكرة لإثبات ذلك أولا).

هذا هو الجزء السهل. الاعتبار الثاني الذي يتم تجاهله هو الواجهة. كيف يتم استخدام المجموعة التي تم إرجاعها؟ هذا مهم لأن الصفيف كله dereference هو كيندا فظيعة في بيرل. علي سبيل المثال:

for my $info (@{ getInfo($some, $args) }) {
    ...
}

هذا قبيح. هذا هو أفضل بكثير.

for my $info ( getInfo($some, $args) ) {
    ...
}

كما أنه يضفي نفسه على رسم الخرائط والتشقق.

my @info = grep { ... } getInfo($some, $args);

ولكن إعادة المرجع الصفيف يمكن أن يكون مفيدا إذا كنت ستحصل على العناصر الفردية:

my $address = getInfo($some, $args)->[2];

هذا أبسط من:

my $address = (getInfo($some, $args))[2];

أو:

my @info = getInfo($some, $args);
my $address = $info[2];

ولكن في هذه المرحلة، يجب عليك السؤال عما إذا كانت inefo هي حقا قائمة أو تجزئة.

my $address = getInfo($some, $args)->{address};

ما يجب ألا تفعله هو getInfo() إرجاع صفيف المرجع في سياق العددية ومجموعة في سياق القائمة. تشوه هذا الاستخدام التقليدي للسياق العددية كطابع صفيف يفاجئ المستخدم.

أخيرا، سأقوم بتوصيل الوحدة الخاصة بي، الطريقة :: التوقيعات, ، لأنه يوفر حل وسط لمرور مراجع الصفيف دون الحاجة إلى استخدام بناء جملة الصفيف.

use Method::Signatures;

method foo(\@args) {
    print "@args";      # @args is not a copy
    push @args, 42;   # this alters the caller array
}

my @nums = (1,2,3);
Class->foo(\@nums);   # prints 1 2 3
print "@nums";        # prints 1 2 3 42

يتم ذلك من خلال سحر البيانات :: الاسم المستعار.

3 تحسينات أخرى من التحسينات ذات الأداء المحتمل إذا كنت تقرأ ملف كامل وارقاط وتخفيفه في صفيف:

  1. قم بإيقاف تشغيل التخزين المؤقت مع Sysread () بدلا من القراءة () (يدوي يحذر حول الخلط)
  2. قبل تمديد الصفيف بتقييم العنصر الأخير - يحفظ مخصصات الذاكرة
  3. استخدم UNPACK () لتقسيم البيانات بسرعة مثل بيانات قناة الرسومات UINT16_T

تتيح تمرير مجموعة المرجع إلى الوظيفة للبرنامج الرئيسي للتعامل مع مجموعة بسيطة أثناء استخدام وظيفة عامل الكتابة مرة واحدة ونسيان "$ @ $" وسهم -> [$ II] نماذج الوصول. كونها c'ish تماما، فمن المحتمل أن تكون سريعة!

لا أعرف شيئا عن بيرل بحيث تكون هذه إجابة محايدة باللغة.

إنه أمر غير فعال بنسخ صفيف من روتين فرعي في برنامج الاتصال. تنشأ عدم الكفاءة في الذاكرة الإضافية المستخدمة والوقت المستغرق لنسخ البيانات من مكان إلى آخر. من ناحية أخرى، للجميع ولكن أكبر صفائف، قد لا تعطي لعنة، وقد تفضل نسخ صفائف للأناقة أو النسيان أو أي سبب آخر.

الحل الفعال هو من أجل الروتين الفرعي لتمرير برنامج الاتصال عنوان الصفيف. كما قلت، ليس لدي أدنى فكرة عن السلوك الافتراضي في بيرل في هذا الصدد. لكن بعض اللغات توفر المبرمج خيار اختيار النهج.

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