একটি লেজ পুনরাবৃত্তি ফাংশন অনুকূলিত হয় তা নিশ্চিত করার জন্য স্কাল টিকা কী?


98

আমি মনে করি যে @tailrecসংকলকটি একটি লেজ পুনরাবৃত্তির ক্রিয়াকে অনুকূল করবে ensure আপনি কি কেবল ঘোষণার সামনে রেখেছেন? স্কেল স্ক্রিপ্টিং মোডে ব্যবহার করা হয় (উদাহরণস্বরূপ, :load <file>আরপিএল এর অধীনে) যদি এটিও কাজ করে ?

উত্তর:


119

" টেইল কল, @ টেলরেক এবং ট্রাম্পোলাইন " ব্লগ পোস্ট থেকে:

  • স্কেলা ২.৮-এ আপনি @tailrecকোন পদ্ধতিগুলি অনুকূলিত হয়েছে সে সম্পর্কে তথ্য পেতে নতুন টিকাটি ব্যবহার করতে সক্ষম হবেন ।
    এই টীকা আপনাকে নির্দিষ্ট পদ্ধতি চিহ্নিত করতে দেয় যা আপনি আশা করেন যে সংকলকটি অনুকূলিত করবে।
    সংকলক দ্বারা সেগুলি অপ্টিমাইজ করা না হলে আপনি তার পরে একটি সতর্কতা পাবেন।
  • স্কেল ২.7 বা তার আগে, কোনও পদ্ধতি অপ্টিমাইজড হয়েছে কিনা তা বের করার জন্য আপনাকে ম্যানুয়াল টেস্টিং বা বাইটকোডের পরিদর্শনের উপর নির্ভর করতে হবে।

উদাহরণ:

আপনি একটি @tailrecটীকা যোগ করতে পারেন যাতে আপনি নিশ্চিত হন যে আপনার পরিবর্তনগুলি কাজ করেছে।

import scala.annotation.tailrec

class Factorial2 {
  def factorial(n: Int): Int = {
    @tailrec def factorialAcc(acc: Int, n: Int): Int = {
      if (n <= 1) acc
      else factorialAcc(n * acc, n - 1)
    }
    factorialAcc(1, n)
  }
}

এবং এটি আরপিএল থেকে কাজ করে (উদাহরণস্বরূপ স্কালার আরপিএল টিপস এবং কৌশলগুলি ):

C:\Prog\Scala\tests>scala
Welcome to Scala version 2.8.0.RC5 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_18).
Type in expressions to have them evaluated.
Type :help for more information.

scala> import scala.annotation.tailrec
import scala.annotation.tailrec

scala> class Tails {
     | @tailrec def boom(x: Int): Int = {
     | if (x == 0) throw new Exception("boom!")
     | else boom(x-1)+ 1
     | }
     | @tailrec def bang(x: Int): Int = {
     | if (x == 0) throw new Exception("bang!")
     | else bang(x-1)
     | }
     | }
<console>:9: error: could not optimize @tailrec annotated method: it contains a recursive call not in tail position
       @tailrec def boom(x: Int): Int = {
                    ^
<console>:13: error: could not optimize @tailrec annotated method: it is neither private nor final so can be overridden
       @tailrec def bang(x: Int): Int = {
                    ^

44

স্কালা সংকলক স্বয়ংক্রিয়ভাবে কোনও সত্যিকারের লেজ-পুনরাবৃত্ত পদ্ধতিটি অনুকূল করে তুলবে। আপনি যদি এমন কোনও পদ্ধতির টীকা দেন যা আপনি মনে করেন যে @tailrecটীকাটি টীকা অনুসারে টীকা অনুসারে পুনরাবৃত্তিযোগ্য , তবে পদ্ধতিটি যদি পুচ্ছ-পুনরাবৃত্তি না করে তবে সংকলক আপনাকে সতর্ক করবে। এটি @tailrecকোনও পদ্ধতি বর্তমানে অনুকূলিত হওয়ার যোগ্য এবং এটি সংশোধিত হওয়ার সাথে সাথে এটি অপ্টিমাইজযোগ্য থেকে যায় তা উভয়ই নিশ্চিত করে তা টীকাটিকে একটি ভাল ধারণা করে তোলে ।

নোট করুন যে স্ক্যালাল কোনও পদ্ধতিকে পুচ্ছ-পুনরাবৃত্ত হিসাবে বিবেচনা করে না যদি এটি ওভাররাইড করা যায়। সুতরাং পদ্ধতিটি অবশ্যই ব্যক্তিগত, চূড়ান্ত, কোনও বস্তুর (শ্রেণি বা বৈশিষ্ট্যের বিপরীতে) হতে হবে, বা অন্য পদ্ধতির অভ্যন্তরে অনুকূলিত হতে হবে।


8
আমি মনে করি এটি overrideজাভাতে টীকাগুলির মতো - কোডটি এটি ছাড়াই কাজ করে, তবে আপনি যদি এটি সেখানে রাখেন তবে এটি আপনাকে বলে যে আপনি কোনও ভুল করেছেন কিনা।
জোল্টন

23

টীকাটি হ'ল scala.annotation.tailrec। পদ্ধতিটি টেল কলটি অনুকূলিত করা যায় না, যদি এটি একটি সংকলক ত্রুটি ট্রিগার করে, যা ঘটে যদি:

  1. পুনরাবৃত্ত কলটি পুচ্ছ অবস্থানে নেই
  2. পদ্ধতিটি ওভাররেড করা যেতে পারে
  3. পদ্ধতিটি চূড়ান্ত নয় (পূর্বের বিশেষ ক্ষেত্রে)

এটি defএকটি পদ্ধতির সংজ্ঞায়নের ঠিক আগে স্থাপন করা হয়েছিল । এটি আরপিএলে কাজ করে।

এখানে আমরা টীকাটি আমদানি করি এবং কোনও পদ্ধতি হিসাবে চিহ্নিত করার চেষ্টা করি @tailrec

scala> import annotation.tailrec
import annotation.tailrec

scala> @tailrec def length(as: List[_]): Int = as match {  
     |   case Nil => 0
     |   case head :: tail => 1 + length(tail)
     | }
<console>:7: error: could not optimize @tailrec annotated method: it contains a recursive call not in tail position
       @tailrec def length(as: List[_]): Int = as match { 
                    ^

উফফফফ! শেষ প্রার্থনা হয় 1.+(), না length()! আসুন পদ্ধতিটি সংস্কার করুন:

scala> def length(as: List[_]): Int = {                                
     |   @tailrec def length0(as: List[_], tally: Int = 0): Int = as match {
     |     case Nil          => tally                                       
     |     case head :: tail => length0(tail, tally + 1)                    
     |   }                                                                  
     |   length0(as)
     | }
length: (as: List[_])Int

নোটটি length0স্বয়ংক্রিয়ভাবে ব্যক্তিগত কারণ এটি অন্য পদ্ধতির স্কোপে সংজ্ঞায়িত করা হয়েছে।


4
আপনি উপরে যা বলেছিলেন তা প্রসারিত করে, স্কালা কেবলমাত্র একটি পদ্ধতির জন্য লেজ কলগুলি অনুকূল করতে পারে। পারস্পরিক পুনরাবৃত্ত কলগুলি অনুকূলিত হবে না।
ধনী ডগর্টি

আমি নীট বাছাই হওয়াটিকে ঘৃণা করি, তবে নীল ক্ষেত্রে আপনার উদাহরণ হিসাবে আপনি সঠিক তালিকার দৈর্ঘ্যের ফাংশনটির জন্য মুলত ফিরে আসতে হবে অন্যথায় আপনি পুনরাবৃত্তি শেষ হওয়ার পরে সর্বদা 0 ফেরত মান হিসাবে পাবেন।
লুসিয়ান এনেচি 15'17
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.