স্ক্যালায় নাম বনাম কল করে কল করুন, স্পষ্টকরণ দরকার


239

আমি এটি বুঝতে পেরেছি, স্কালায় কোনও ফাংশন হয় বলা যেতে পারে

  • বাই-মান বা
  • নামে

উদাহরণস্বরূপ, নিম্নলিখিত ঘোষণাগুলি প্রদত্ত, আমরা কীভাবে ফাংশনটি বলা হবে তা জানি?

ঘোষণা:

def  f (x:Int, y:Int) = x;

কল

f (1,2)
f (23+55,5)
f (12+3, 44*11)

নিয়ম কি দয়া করে?

উত্তর:


540

আপনি যে উদাহরণটি দিয়েছেন তা কেবল কল-বাই-মান ব্যবহার করে, তাই আমি একটি নতুন, সরল উদাহরণ দেব, যা পার্থক্য দেখায়।

প্রথমে, ধরে নেওয়া যাক আমাদের একটি পার্শ্ব-প্রতিক্রিয়াযুক্ত একটি ফাংশন রয়েছে। এই ফাংশনটি কিছু প্রিন্ট করে এবং তারপরে একটি প্রদান করে Int

def something() = {
  println("calling something")
  1 // return value
}

এখন আমরা দুটি ফাংশন সংজ্ঞায়িত করতে যাচ্ছি যেগুলি Intযুক্তিগুলি গ্রহণ করে ঠিক একইভাবে ব্যতীত একজন কল-বাই-মান স্টাইলে ( x: Int) এবং অন্যটি কল-বাই-নাম শৈলীতে ( x: => Int) দেয়।

def callByValue(x: Int) = {
  println("x1=" + x)
  println("x2=" + x)
}

def callByName(x: => Int) = {
  println("x1=" + x)
  println("x2=" + x)
}

এখন যখন আমরা তাদের আমাদের পার্শ্ব-কার্যকারী ফাংশন দিয়ে ডাকি তখন কী হয়?

scala> callByValue(something())
calling something
x1=1
x2=1

scala> callByName(something())
calling something
x1=1
calling something
x2=1

সুতরাং আপনি দেখতে পাচ্ছেন যে কল-বাই-মান সংস্করণে, পাস-ইন ফাংশন কল ( something()) এর পার্শ্ব-প্রতিক্রিয়াটি কেবল একবার ঘটেছে। তবে, কল-বাই-নাম সংস্করণে, পার্শ্ব-প্রতিক্রিয়াটি দু'বার হয়েছিল।

এটি কারণ কল-বাই-মান ফাংশনগুলি ফাংশনটি বলার আগে পাস-ইন এক্সপ্রেশনটির মানটি গণনা করে, সুতরাং প্রতিবার একই মানটি অ্যাক্সেস করা হয়। পরিবর্তে, কল-টু-নাম ফাংশনগুলি যতবার প্রবেশ করা যায় ততবার পাস-ইন এক্সপ্রেশনটির মানটিকে পুনরায় সঞ্চার করে।


296
আমি সবসময় ভেবেছিলাম এই পরিভাষাটি অযথা বিভ্রান্তিকর। কোনও ফাংশনে একাধিক প্যারামিটার থাকতে পারে যা তাদের কল-বাই নাম বনাম কল-বাই-মান স্থিতিতে পরিবর্তিত হয়। সুতরাং এটি একটি যে না ফাংশন কল-বাই-নাম অথবা কল-বাই-মান, এটা যে তার প্রতিটি এর পরামিতি হতে পারে পাস -by-নাম বা পাস-বাই-মান। উপরন্তু, "কল-বাই-নাম" সঙ্গে কিছুই করার আছে নাম=> Intএর থেকে আলাদা ধরনেরInt ; এটি Int"বনাম জাস্ট" কোনও আর্গুমেন্টের ক্রিয়াকলাপ Int। একবার আপনি প্রথম শ্রেণীর ফাংশন পেয়েছেন আপনি না প্রয়োজন উদ্ভাবিত থেকে কল-বাই-নাম পরিভাষা এই বর্ণনা করতে।
বেন

2
@ বেন, এটি কয়েকটি প্রশ্নের উত্তর দিতে সহায়তা করে, ধন্যবাদ। আমি আরও লিখতে চাই যে পাস-বাই-নামের শব্দার্থক শব্দটি এটি পরিষ্কারভাবে ব্যাখ্যা করেছে।
ক্রিস্টোফার পোইল

3
@ সেলিমঅবার যদি পাঠ্যটি f(2)টাইপের অভিব্যক্তি হিসাবে সংকলিত হয় Int, উত্পন্ন কোডটি fআর্গুমেন্টের সাথে কল করে 2এবং ফলাফলটি হ'ল অভিব্যক্তির মান। যদি সেই একই পাঠ্যটি টাইপের একটি এক্সপ্রেশন হিসাবে সংকলিত হয় => Intতবে উত্পন্ন কোডটি কোনও ধরণের "কোড ব্লক" এর সাথে রেফারেন্সের মান হিসাবে একটি রেফারেন্স ব্যবহার করে। যেভাবেই হোক না কেন, সেই ধরণের একটি মান সেই ধরণের একটি পরামিতি প্রত্যাশা করে কোনও ফাংশনে পাঠানো যেতে পারে। আমি নিশ্চিত যে আপনি ভেরিয়েবল অ্যাসাইনমেন্ট সহ এটি করতে পারবেন, কোনও প্যারামিটার চোখে না পরে। তাহলে এর সাথে নাম বা কল করার কিছু আছে কি?
বেন

4
@ বেন তাই যদি => Int"কোনও ইন্টারটি উত্পন্ন করে এমন কোনও আর্গুমেন্টের ফাংশন" হয় তবে কীভাবে এটি আলাদা () => Int? স্কেলা এগুলি আলাদাভাবে আচরণ করে বলে মনে হয়, উদাহরণস্বরূপ => Intদৃশ্যমানভাবে কোনও valপ্যারামিটারের ধরণের হিসাবে কাজ করে না ।
টিম গুডম্যান

5
@ টিম গুডম্যান আপনি ঠিক বলেছেন, আমার তৈরির চেয়ে এটি কিছুটা জটিল। => Intএকটি সুবিধা, এবং এটি কোনও ফাংশন অবজেক্টের মতো হুবহু বাস্তবায়িত হয় না (সম্ভবত আপনার ধরণের ভেরিয়েবল কেন না থাকতে পারে => Int, যদিও এটি কাজ করতে পারে না তার কোনও মৌলিক কারণ নেই)। () => Intহয় স্পষ্টভাবে কোন যুক্তি যে ফিরে আসবে একটি ফাংশন Int, যা স্পষ্টভাবে বলা হবে প্রয়োজন এবং একটি ফাংশন হিসাবে প্রেরণ করা সম্ভব। => Intএটি "প্রক্সি Int" বাছাই করা এবং আপনি এটির সাথে একমাত্র যা করতে পারেন তা হ'ল এটি (স্পষ্টভাবে) পাওয়ার জন্য Int
বেন

51

মার্টিন ওডারস্কির উদাহরণ এখানে:

def test (x:Int, y: Int)= x*x

আমরা মূল্যায়ন কৌশলটি পরীক্ষা করতে এবং এই পরিস্থিতিতে কোনটি দ্রুত (কম পদক্ষেপ) দ্রুত নির্ধারণ করতে চাই:

test (2,3)

মূল্য অনুসারে কল করুন: পরীক্ষার (2,3) -> 2 * 2 ->
নাম অনুসারে 4 কল করুন: পরীক্ষা (2,3) -> 2 * 2 -> 4
এখানে ফলাফলটি একই সংখ্যক পদক্ষেপ নিয়ে পৌঁছেছে।

test (3+4,8)

মূল্য অনুসারে কল করুন: পরীক্ষা (7,8) -> 7 * 7 -> 49
নামে কল করুন: (3 + 4) (3 + 4) -> 7 (3 + 4) -> 7 * 7 -> 49
এখানে কল করুন মান দ্বারা দ্রুত হয়।

test (7,2*4)

মূল্য অনুসারে কল করুন: পরীক্ষা (7,8) -> 7 * 7 -> 49
নাম দিয়ে কল করুন: 7 * 7 -> 49
এখানে নাম দ্বারা কলটি দ্রুত

test (3+4, 2*4) 

মূল্য অনুসারে কল করুন: পরীক্ষা (7,2 * 4) -> পরীক্ষা (7, 8) -> 7 * 7 -> 49
নামে কল করুন: (3 + 4) (3 + 4) -> 7 (3 + 4) -> 7 * 7 -> 49
ফলাফল একই পদক্ষেপের মধ্যে পৌঁছেছে।


1
সিবিভির তৃতীয় উদাহরণে, আমি মনে করি আপনি পরীক্ষার পরিবর্তে (7,8) পরীক্ষা (7,14) বলতে চেয়েছিলেন
টালোনক্স

1
উদাহরণ স্কোর প্রোগ্রামিংয়ের নীতি, কুরসেরা থেকে নেওয়া। বক্তৃতা 1.2। নামে কলটিতে def test (x:Int, y: => Int) = x * xনোটটি পড়তে হবে যে প্যারামিটার y কখনও ব্যবহৃত হয় না।
ডাঃ জেরি

1
ভালো উদাহরণ! কোর্সেরা
এমওসি

এটি পার্থক্যের একটি ভাল ব্যাখ্যা, তবে জিজ্ঞাসা করা প্রশ্নের উত্তর দিচ্ছে না, যথা দুজনের মধ্যে কে স্কালা কল করছে
db1234

16

আপনার উদাহরণের ক্ষেত্রে সমস্ত পরামিতিগুলি ফাংশনটিতে ডাকা হওয়ার আগে মূল্যায়ন করা হবে, কারণ আপনি কেবল মান দ্বারা এগুলি নির্ধারণ করছেন । আপনি যদি নাম দিয়ে আপনার পরামিতিগুলি সংজ্ঞায়িত করতে চান তবে আপনার একটি কোড ব্লক পাস করা উচিত:

def f(x: => Int, y:Int) = x

এই পরামিতিটি ফাংশনে না বলা পর্যন্তx মূল্যায়ন করা হবে না ।

এই ছোট্ট পোস্টটি এখানে খুব সুন্দরভাবে ব্যাখ্যা করে।


10

উপরের মন্তব্যে @ বেনের বক্তব্য পুনরুক্ত করার জন্য, "কল-বাই-নাম" কেবল সিনট্যাকটিক চিনি হিসাবে ভাবা ভাল বলে আমি মনে করি। পার্সার কেবল বেনামে ফাংশনগুলিতে এক্সপ্রেশনগুলিকে আবৃত করে, যাতে সেগুলি পরে ব্যবহৃত হয়, যখন সেগুলি ব্যবহৃত হয়।

ফলস্বরূপ, সংজ্ঞায়নের পরিবর্তে

def callByName(x: => Int) = {
  println("x1=" + x)
  println("x2=" + x)
}

এবং চলমান:

scala> callByName(something())
calling something
x1=1
calling something
x2=1

আপনি আরও লিখতে পারেন:

def callAlsoByName(x: () => Int) = {
  println("x1=" + x())
  println("x2=" + x())
}

এবং একই প্রভাবের জন্য নিম্নলিখিত হিসাবে এটি চালান:

callAlsoByName(() => {something()})

calling something
x1=1
calling something
x2=1

আমি মনে করি আপনি বোঝাতে চেয়েছিলেন: <! - ভাষা: ল্যাং-স্কালা -> ডিফ কলআলোসোবাইনেম (x: () => আন্ত) = {প্রিন্টন ("x1 =" + এক্স ()) প্রিন্টলান ("x2 =" + এক্স ( ))} এবং তারপরে: <! - ভাষা: ল্যাং-জেএস -> কলএলসোবাইনেম (() => কিছু ()) এই শেষ কলটিতে আপনার কোনও কিছুর চারদিকে কোঁকড়ানো ধনুর্বন্ধনী প্রয়োজন বলে আমি মনে করি না। দ্রষ্টব্য: আমি কেবল আপনার উত্তর সম্পাদনা করার চেষ্টা করেছি তবে আমার সম্পাদনাটি পর্যালোচকরা বলেছিল এটির পরিবর্তে কোনও মন্তব্য বা পৃথক উত্তর হওয়া উচিত।
lambdista

স্পষ্টতই আপনি মন্তব্যগুলিতে হাইলাইটিং সিনট্যাক্স ব্যবহার করতে পারবেন না তাই কেবল "<! - ভাষা: ল্যাং-স্কালা ->" অংশটিকে উপেক্ষা করুন! আমি আমার নিজস্ব মন্তব্য সম্পাদনা করতে পারতাম তবে আপনাকে কেবল 5 মিনিটের মধ্যে এটি করার অনুমতি দেওয়া হয়েছে! :)
ল্যাম্বডিস্টা

1
আমি সম্প্রতি এটি মধ্যে দৌড়ে। এটি ধারণার মতো মনে করা ঠিক আছে তবে স্কালার মধ্যে => Tএবং এর মধ্যে পার্থক্য রয়েছে () => T। একটি ফাংশন যা প্যারামিটার হিসাবে প্রথম ধরণ নেয়, দ্বিতীয়টি গ্রহণ করবে না, স্কালার জন্য এগুলি @ScalaSignatureসংকলনের সময় ত্রুটি নিক্ষেপ করতে টীকাতে পর্যাপ্ত তথ্য সংরক্ষণ করে । উভয়ের জন্য বাইকোড => Tএবং () => Tযদিও একই এবং এটি একটি Function0। আরও তথ্যের জন্য এই প্রশ্নটি দেখুন ।
vsnyc

6

আমি কেবল একটি উদাহরণ সরবরাহ না করে একটি সাধারণ ব্যবহারের ক্ষেত্রে ব্যাখ্যা করার চেষ্টা করব

কল্পনা করুন যে আপনি একটি "নগার অ্যাপ" তৈরি করতে চান যা প্রতিবারের পরে আপনাকে আটকে দেবে Nag

নিম্নলিখিত বাস্তবায়ন পরীক্ষা করুন:

object main  {

    def main(args: Array[String]) {

        def onTime(time: Long) {
            while(time != time) println("Time to Nag!")
            println("no nags for you!")
        }

        def onRealtime(time: => Long) {
            while(time != time) println("Realtime Nagging executed!")
        }

        onTime(System.nanoTime())
        onRealtime(System.nanoTime())
    }
}

উপরোক্ত প্রয়োগে নাগর কেবল তখন নাম দ্বারা পাস করার সময় কাজ করবে কারণ, যখন মান দিয়ে পাস করার সময় এটি পুনরায় ব্যবহৃত হবে এবং সুতরাং নামটি পাস করার সাথে সাথে মানটি পুনরায় মূল্যায়ন করা হবে না সময় ভেরিয়েবল অ্যাক্সেস করা হয়


4

সাধারণত, ফাংশনগুলির পরামিতিগুলি হ'ল বাই-মান পরামিতি; অর্থাৎ প্যারামিটারটির মানটি ফাংশনে যাওয়ার আগে নির্ধারিত হয়। তবে আমাদের যদি এমন কোনও ফাংশন লিখতে হবে যা পরামিতি হিসাবে গ্রহণ করে এমন একটি অভিব্যক্তি যা আমাদের ফাংশনটির মধ্যে না বলা পর্যন্ত আমরা মূল্যায়ন করতে চাই না? এই পরিস্থিতিতে, স্কালা কল-বাই নাম পরামিতি অফার করে।

একটি কল-টু-নাম মেকানিজম কলিকে একটি কোড ব্লক পাস করে এবং প্রতিবার কলি প্যারামিটারটি অ্যাক্সেস করে, কোড ব্লকটি কার্যকর করা হয় এবং মান গণনা করা হয়।

object Test {
def main(args: Array[String]) {
    delayed(time());
}

def time() = {
  println("Getting time in nano seconds")
  System.nanoTime
}
def delayed( t: => Long ) = {
  println("In delayed method")
  println("Param: " + t)
  t
}
}
 1. সি: /> স্ক্যালাক টেস্ট.স্কালা 
 2. স্কাল টেস্ট
 3. বিলম্বিত পদ্ধতিতে
 ৪. ন্যানো সেকেন্ডে সময় পাচ্ছেন
 5. পরম: 81303808765843
 N. ন্যানো সেকেন্ডে সময় পাচ্ছে

2

আমি ধরে নিই, call-by-valueউপরে আলোচনা হিসাবে ফাংশনটি কেবল ফাংশনটির মানগুলিকেই পাস করে। মতে Martin Oderskyএটি একটি মূল্যায়ন কৌশল যা একটি স্কাল অনুসরণ করে যা ফাংশন মূল্যায়নে গুরুত্বপূর্ণ ভূমিকা পালন করে। তবে, এটিকে সহজ করুন call-by-name। এটি একটি পাস মত ফাংশন একটি যুক্তি হিসাবে পদ্ধতি হিসাবে জানেন Higher-Order-Functions। পদ্ধতিটি পাস হওয়া প্যারামিটারের মানটি অ্যাক্সেস করলে এটি পাস ফাংশনগুলির বাস্তবায়নকে কল করে। নীচের হিসাবে:

@ ডিএডিজি উদাহরণ অনুসারে প্রথমে পদ্ধতিটি তৈরি করুন:

def something() = {
 println("calling something")
 1 // return value
}  

এই ফাংশনটিতে একটি printlnবিবৃতি থাকে এবং পূর্ণসংখ্যার মান প্রদান করে। ফাংশনটি তৈরি করুন, যার একটি হিসাবে যুক্তি রয়েছে call-by-name:

def callByName(x: => Int) = {
 println("x1=" + x)
 println("x2=" + x)
}

এই ফাংশন প্যারামিটার, একটি বেনামি ফাংশন সংজ্ঞায়িত করা হয়েছে যারা একটি পূর্ণসংখ্যার মান ফিরে পেয়েছে। এতে xফাংশনটির সংজ্ঞা রয়েছে যারা 0আর্গুমেন্টগুলি পাস করেছে তবে রিটার্ন intমান এবং আমাদের somethingফাংশনে একই স্বাক্ষর রয়েছে। আমরা যখন ফাংশনটি কল করি তখন আমরা যুক্তি হিসাবে ফাংশনটি পাস করি callByName। তবে এর ক্ষেত্রে call-by-valueশুধুমাত্র ফাংশনে পূর্ণসংখ্যার মানটি পাস করুন। আমরা নীচে হিসাবে ফাংশন কল:

scala> callByName(something())
 calling something
 x1=1
 calling something
 x2=1 

এই আমাদের somethingপদ্ধতি দুইবার নামক কারণ যখন আমরা মান অ্যাক্সেস করতে, xমধ্যে callByName, পদ্ধতির defintion তার কল somethingপদ্ধতি।


2

মান দ্বারা কল সাধারণ ব্যবহারের কেস হিসাবে এখানে অনেক উত্তর দ্বারা ব্যাখ্যা করা হয় ..

কল-টু-নাম কলারের কাছে একটি কোড ব্লক পাস করে এবং প্রতিবার কলার প্যারামিটারটি অ্যাক্সেস করে, কোড ব্লকটি কার্যকর করা হয় এবং মান গণনা করা হয়।

আমি নীচে ব্যবহারের কেসগুলির সাথে আরও সহজ উপায়ে কলটি প্রদর্শনের চেষ্টা করব

উদাহরণ 1:

নাম অনুসারে কল করার সহজ উদাহরণ / ব্যবহারের ক্ষেত্রে নীচে ফাংশন থাকে যা প্যারামিটার হিসাবে ফাংশন নেয় এবং সময় কেটে যায়।

 /**
   * Executes some code block and prints to stdout the 
time taken to execute   the block 
for interactive testing and debugging.
   */
  def time[T](f: => T): T = {
    val start = System.nanoTime()
    val ret = f
    val end = System.nanoTime()

    println(s"Time taken: ${(end - start) / 1000 / 1000} ms")

    ret
  }

উদাহরণ 2:

অ্যাপাচি স্পার্ক (স্কালাসহ) নাম ব্যবহার করে কল ব্যবহার করে লগিং ব্যবহার করে এমন Loggingবৈশিষ্ট্য দেখুন যা এর অলসভাবেlog.isInfoEnabled নীচের পদ্ধতি থেকে নিরীক্ষণ করে কিনা তা মূল্যায়ন করে ।

protected def logInfo(msg: => String) {
     if (log.isInfoEnabled) log.info(msg)
 }

2

একটি কল বাই মান দ্বারা , ফাংশন কল করার সময় এক্সপ্রেশনটির মান প্রাক-গণনা করা হয় এবং সেই নির্দিষ্ট মানটিকে সংশ্লিষ্ট ফাংশনটির প্যারামিটার হিসাবে পাস করা হয়। একই মানটি সমস্ত ফাংশন জুড়ে ব্যবহৃত হবে।

একটি যেহেতু নাম ধরে ডাকেন প্রকাশের নিজেই ফাংশন প্যারামিটার হিসেবে পাস করা হয়েছে এবং এটি শুধুমাত্র ফাংশন, যখনই সেই নির্দিষ্ট পরামিতি বলা হয় ভিতরে নির্ণয় করা হয়।

কল দ্বারা নাম এবং কল মধ্যে মান মধ্যে স্ক্যাল মধ্যে পার্থক্য নীচের উদাহরণ দিয়ে আরও ভাল বোঝা যেতে পারে:

টুকিটাকি সংকেতলিপি

object CallbyExample extends App {

  // function definition of call by value
  def CallbyValue(x: Long): Unit = {
    println("The current system time via CBV: " + x);
    println("The current system time via CBV " + x);
  }

  // function definition of call by name
  def CallbyName(x: => Long): Unit = {
    println("The current system time via CBN: " + x);
    println("The current system time via CBN: " + x);
  }

  // function call
  CallbyValue(System.nanoTime());
  println("\n")
  CallbyName(System.nanoTime());
}

আউটপুট

The current system time via CBV: 1153969332591521
The current system time via CBV 1153969332591521


The current system time via CBN: 1153969336749571
The current system time via CBN: 1153969336856589

উপরের কোড স্নিপেটে, কলবিভ্যালু (System.nanoTime ()) ফাংশন কল করার জন্য , সিস্টেম ন্যানো সময়টি প্রাক-গণনা করা হয় এবং সেই প্রাক-গণনা করা মানটি ফাংশন কলটিতে একটি প্যারামিটার পাস করা হয়েছে।

কিন্তু কলবিনেম (System.nanoTime ()) ফাংশন কলে, "System.nanoTime ())" এক্সপ্রেশনটি নিজেই ফাংশন কলটিতে প্যারামিটার হিসাবে পাস হয় এবং সেই ফাংশনের অভ্যন্তরে যখন প্যারামিটারটি ব্যবহার করা হয় তখন সেই অভিব্যক্তির মান গণনা করা হয় ।

কলবিনেম ফাংশনটির ফাংশন সংজ্ঞাটি লক্ষ্য করুন, যেখানে প্যারামিটার x এবং এর ডেটাটাইপ পৃথক করে একটি => চিহ্ন রয়েছে । সেখানে নির্দিষ্ট চিহ্নটি ফাংশনটি নির্দেশ করে যা নাম টাইপ করে কল করা।

অন্য কথায়, ফাংশনে প্রবেশের আগে একবার মান ফাংশন আর্গুমেন্টের মাধ্যমে কলটি মূল্যায়ন করা হয়, তবে নাম ফাংশন আর্গুমেন্টের মাধ্যমে কলটি যখন প্রয়োজন হয় কেবল তখনই ফাংশনের অভ্যন্তরে মূল্যায়ন করা হয়।

আশাকরি এটা সাহায্য করবে!


2

এখানে আমার একজন সহকর্মী যিনি বর্তমানে স্কালার কোর্সটি নিচ্ছেন তাদের সহায়তা করার জন্য আমি একটি দ্রুত উদাহরণ প্রদান করেছি। যা আমি মজাদার মনে করেছি তা হ'ল মার্টিন বক্তৃতাটিতে উপস্থাপিত && প্রশ্ন উত্তরটিকে উদাহরণ হিসাবে ব্যবহার করেন নি। যে কোনও ইভেন্টে আমি আশা করি এটি সাহায্য করবে।

val start = Instant.now().toEpochMilli

val calc = (x: Boolean) => {
    Thread.sleep(3000)
    x
}


def callByValue(x: Boolean, y: Boolean): Boolean = {
    if (!x) x else y
}

def callByName(x: Boolean, y: => Boolean): Boolean = {
    if (!x) x else y
}

new Thread(() => {
    println("========================")
    println("Call by Value " + callByValue(false, calc(true)))
    println("Time " + (Instant.now().toEpochMilli - start) + "ms")
    println("========================")
}).start()


new Thread(() => {
    println("========================")
    println("Call by Name " + callByName(false, calc(true)))
    println("Time " + (Instant.now().toEpochMilli - start) + "ms")
    println("========================")
}).start()


Thread.sleep(5000)

কোডের আউটপুট নিম্নলিখিত হবে:

========================
Call by Name false
Time 64ms
========================
Call by Value false
Time 3068ms
========================

1

প্যারামিটারগুলি সাধারণত মান দ্বারা পাস হয়, যার অর্থ ফাংশন বডিটিতে প্রতিস্থাপনের আগে সেগুলি মূল্যায়ন করা হবে।

ফাংশনটি সংজ্ঞায়িত করার সময় আপনি ডাবল তীর ব্যবহার করে প্যারামিটারটিকে নাম ধরে ডাকতে বাধ্য করতে পারেন।

// first parameter will be call by value, second call by name, using `=>`
def returnOne(x: Int, y: => Int): Int = 1

// to demonstrate the benefits of call by name, create an infinite recursion
def loop(x: Int): Int = loop(x)

// will return one, since `loop(2)` is passed by name so no evaluated
returnOne(2, loop(2))

// will not terminate, since loop(2) will evaluate. 
returnOne(loop(2), 2) // -> returnOne(loop(2), 2) -> returnOne(loop(2), 2) -> ... 

1

ইন্টারনেটে এই প্রশ্নের জন্য ইতিমধ্যে প্রচুর চমত্কার উত্তর রয়েছে। আমি এই বিষয়টি সম্পর্কে যে সমস্ত ব্যাখ্যা এবং উদাহরণ সংগ্রহ করেছি তার সংকলন লিখব, যদি কারও পক্ষে এটির সহায়ক হতে পারে তবে

সূচনা

কল-বাই-মান (সিবিভি)

সাধারণত, ফাংশনগুলির পরামিতিগুলি কল-বাই-মান পরামিতি হয়; অর্থাৎ প্যারামিটারগুলি ফাংশনটি নিজেই মূল্যায়নের আগে তাদের মান নির্ধারণ করতে বাম থেকে ডানদিকে মূল্যায়ন করা হয়

def first(a: Int, b: Int): Int = a
first(3 + 4, 5 + 6) // will be reduced to first(7, 5 + 6), then first(7, 11), and then 7

কল-বাই-নাম (সিবিএন)

তবে আমাদের যদি এমন কোনও ফাংশন লিখতে হবে যা পরামিতি হিসাবে এমন একটি অভিব্যক্তি হিসাবে গ্রহণ করে যা আমাদের ফাংশনটির মধ্যে না বলা পর্যন্ত মূল্যায়ন না করে? এই পরিস্থিতিতে, স্কালা কল-বাই নাম পরামিতি অফার করে। অর্থাত প্যারামিটারটি যেমন হয় তেমন কার্যক্রমে চলে যায় এবং এর মূল্যায়ন প্রতিস্থাপনের পরে ঘটে

def first1(a: Int, b: => Int): Int = a
first1(3 + 4, 5 + 6) // will be reduced to (3 + 4) and then to 7

একটি কল-টু-নাম মেকানিজম কলটিতে একটি কোড ব্লককে পাস করে এবং প্রতিবার কল প্যারামিটারে প্রবেশ করে, কোড ব্লকটি কার্যকর করা হয় এবং মান গণনা করা হয়। নিম্নলিখিত উদাহরণে, বিলম্বিত একটি বার্তা প্রিন্ট করে যা দেখায় যে পদ্ধতিটি প্রবেশ করা হয়েছে। এরপরে, বিলম্বিত একটি বার্তা এর মান সহ মুদ্রণ করে। অবশেষে, বিলম্বিত রিটার্ন 'টি':

 object Demo {
       def main(args: Array[String]) {
            delayed(time());
       }
    def time() = {
          println("Getting time in nano seconds")
          System.nanoTime
       }
       def delayed( t: => Long ) = {
          println("In delayed method")
          println("Param: " + t)
       }
    }

বিলম্বিত পদ্ধতিতে
ন্যানো সেকেন্ডে সময় পাওয়া
পরম: 2027245119786400

প্রতিটি ক্ষেত্রে জন্য পেশাদার এবং কনস

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

সিবিভি: + এটি প্রায়শই সিবিএন এর চেয়ে তাত্পর্যপূর্ণভাবে আরও দক্ষ হয়, কারণ এটি পুনরাবৃত্তি যুক্তি প্রকাশের পুনরাবৃত্তিটি এড়িয়ে যায় যা নাম দ্বারা ডাকে। এটি প্রতিটি ফাংশন আর্গুমেন্টকে একবারে মূল্যায়ন করে + এটি অপরিহার্য প্রভাব এবং পার্শ্ব প্রতিক্রিয়াগুলির সাথে অনেক ভাল খেলে, কারণ আপনার অভিব্যক্তিগুলি যখন মূল্যায়ন করা হয় তখন আপনি আরও ভাল করে জানতে চান। এটির পরামিতিগুলির মূল্যায়নের সময় এটি লুপ হতে পারে * উপরের সমাপ্তির নীচে চেক *

সমাপ্তির গ্যারান্টি না থাকলে কী হবে?

-যদি কোনও অভিব্যক্তির সিবিভির মূল্যায়ন ই শেষ হয়ে যায়, তারপরে সিবিএন মূল্যায়নও ই সমাপ্ত করে দেয় - অন্য দিকটি সত্য নয়

অবসানহীন উদাহরণ

def first(x:Int, y:Int)=x

প্রথমে ভাবটি বিবেচনা করুন (1, লুপ)

সিবিএন: প্রথম (1, লুপ) → 1 সিবিভি: প্রথম (1, লুপ) expression এই অভিব্যক্তিটির যুক্তিগুলি হ্রাস করুন। যেহেতু একটি লুপ, এটি যুক্তিগুলি অনন্যভাবে হ্রাস করে। এটি শেষ হয় না

প্রতিটি ক্ষেত্রে আচরণের বিভিন্ন

আসুন যে একটি পদ্ধতি পরীক্ষা সংজ্ঞায়িত করা যাক

Def test(x:Int, y:Int) = x * x  //for call-by-value
Def test(x: => Int, y: => Int) = x * x  //for call-by-name

কেস 1 পরীক্ষা (2,3)

test(2,3)2*24

যেহেতু আমরা ইতিমধ্যে মূল্যায়িত যুক্তি দিয়ে শুরু করি এটি কল-বাই-মান এবং কল-বাই নাম অনুসারে সমান পরিমাণ পদক্ষেপ হবে

কেস 2 পরীক্ষা (3 + 4,8)

call-by-value: test(3+4,8) → test(7,8)7 * 749
call-by-name: (3+4)*(3+4)7 * (3+4)7 * 749

এই ক্ষেত্রে কল-বাই-মান কম পদক্ষেপগুলি সম্পাদন করে

কেস 3 পরীক্ষা (7, 2 * 4)

call-by-value: test(7, 2*4) → test(7,8)7 * 749
call-by-name: (7)*(7)49

আমরা দ্বিতীয় তর্কটির অপ্রয়োজনীয় গণনা এড়িয়ে চলি

কেস 4 পরীক্ষা (3 + 4, 2 * 4)

call-by-value: test(7, 2*4) → test(7,8)7 * 749
call-by-name: (3+4)*(3+4)7*(3+4)7*749

বিভিন্ন পদ্ধতির

প্রথমে, ধরে নেওয়া যাক আমাদের একটি পার্শ্ব-প্রতিক্রিয়াযুক্ত একটি ফাংশন রয়েছে। এই ফাংশনটি কিছু প্রিন্ট করে এবং তারপরে একটি int প্রদান করে।

def something() = {
  println("calling something")
  1 // return value
}

এখন আমরা দুটি ফাংশন সংজ্ঞায়িত করতে যাচ্ছি যে আন্ত আর্গুমেন্টগুলি গ্রহণ করে ঠিক সেইগুলি ব্যতীত একজন কল-বাই-মান শৈলীতে (x: Int) এবং অন্যটি কল-বাই-নাম শৈলীতে (x: => ইনট)

def callByValue(x: Int) = {
  println("x1=" + x)
  println("x2=" + x)
}
def callByName(x: => Int) = {
  println("x1=" + x)
  println("x2=" + x)
}

এখন যখন আমরা তাদের আমাদের পার্শ্ব-কার্যকারী ফাংশন দিয়ে ডাকি তখন কী হয়?

scala> callByValue(something())
calling something
x1=1
x2=1
scala> callByName(something())
calling something
x1=1
calling something
x2=1

সুতরাং আপনি দেখতে পাচ্ছেন যে কল-বাই-মান সংস্করণে, পাস-ইন ফাংশন কল (কিছু ()) এর পার্শ্ব-প্রতিক্রিয়াটি কেবল একবার হয়েছিল। তবে, কল-বাই-নাম সংস্করণে, পার্শ্ব-প্রতিক্রিয়াটি দু'বার হয়েছিল।

এটি কারণ কল-বাই-মান ফাংশনগুলি ফাংশনটি বলার আগে পাস-ইন এক্সপ্রেশনটির মানটি গণনা করে, সুতরাং প্রতিবার একই মানটি অ্যাক্সেস করা হয়। যাইহোক, কল-টু-নাম ফাংশনগুলি যতবার প্রবেশ করা যায় ততবার পাস-ইন এক্সপ্রেশনটির মানটিকে পুনরায় সঞ্চার করে।

নামটি দিয়ে কল করুন - এটি ব্যবহারের চেয়ে ভাল যেখানে উদাহরণ

থেকে: https://stackoverflow.com/a/19036068/1773841

সাধারণ সম্পাদনের উদাহরণ: লগিং।

এর মত একটি ইন্টারফেস কল্পনা করা যাক:

trait Logger {
  def info(msg: => String)
  def warn(msg: => String)
  def error(msg: => String)
}

এবং তারপরে এটি ব্যবহার করা হয়েছে:

logger.info("Time spent on X: " + computeTimeSpent)

যদি তথ্য পদ্ধতিটি কিছু না করে (কারণ, বলুন যে লগিং স্তরটি তার চেয়ে বেশিের জন্য কনফিগার করা হয়েছিল), তবে কমপিউটিটাইমস্পেন্টকে কখনই সময় বলা হয় না, সময় সাশ্রয় করতে হবে। লগারের সাথে এটি অনেক ঘটে, যেখানে কেউ প্রায়শই স্ট্রিং ম্যানিপুলেশন দেখেন যা লগ হওয়া কাজের তুলনায় ব্যয়বহুল হতে পারে।

সঠিকতার উদাহরণ: লজিক অপারেটরগুলি।

আপনি সম্ভবত কোডটি দেখেছেন:

if (ref != null && ref.isSomething)

কল্পনা করুন যে আপনি && পদ্ধতিটি এর মতো ঘোষণা করবেন:

trait Boolean {
  def &&(other: Boolean): Boolean
}

তারপরে, যখনই রেফ শালীন হয়, আপনি একটি ত্রুটি পাবেন কারণ && এ যাওয়ার আগে কিছুকে নালারফেরেন্সের জন্য ডাকা হবে। এই কারণে, আসল ঘোষণাটি হ'ল:

trait Boolean {
  def &&(other: => Boolean): Boolean =
    if (this) this else other
}

1

উদাহরণের মধ্য দিয়ে যাওয়া আপনাকে পার্থক্যটি আরও ভালভাবে বুঝতে সহায়তা করবে।

আসুন একটি সাধারণ ক্রিয়া যা বর্তমান সময়কে ফিরিয়ে দেয়:

def getTime = System.currentTimeMillis

এখন আমরা নাম দ্বারা একটি ফাংশন সংজ্ঞায়িত করব , যা একটি সেকেন্ডের মধ্যে দু'বার দেরি করে:

def getTimeByName(f: => Long) = { println(f); Thread.sleep(1000); println(f)}

এবং মান অনুসারে এক :

def getTimeByValue(f: Long) = { println(f); Thread.sleep(1000); println(f)}

এখন প্রতিটি কল:

getTimeByName(getTime)
// prints:
// 1514451008323
// 1514451009325

getTimeByValue(getTime)
// prints:
// 1514451024846
// 1514451024846

ফলাফল পার্থক্য ব্যাখ্যা করা উচিত। স্নিপেট এখানে পাওয়া যায়


0

CallByNameযখন ব্যবহার callByValueকরা হয় তখনই অনুরোধ করা হয় এবং যখনই বিবৃতিটির মুখোমুখি হয়।

উদাহরণ স্বরূপ:-

আমার কাছে অসীম লুপ রয়েছে আপনি যদি এই ফাংশনটি সম্পাদন করেন তবে আমরা কখনই scalaপ্রম্পট পাব না ।

scala> def loop(x:Int) :Int = loop(x-1)
loop: (x: Int)Int

একটি callByNameফাংশন উপরোক্ত loopপদ্ধতিটিকে আর্গুমেন্ট হিসাবে গ্রহণ করে এবং এটি কখনই তার দেহের অভ্যন্তরে ব্যবহৃত হয় না।

scala> def callByName(x:Int,y: => Int)=x
callByName: (x: Int, y: => Int)Int

callByNameপদ্ধতির প্রয়োগের সময় আমরা কোনও সমস্যা পাই না (আমরা scalaপ্রম্পট ফিরে পাই) কারণ আমরা ফাংশনের অভ্যন্তরে লুপ ফাংশনটি ব্যবহার করি না callByName

scala> callByName(1,loop(10))
res1: Int = 1
scala> 

কোনও callByValueফাংশন loopপ্যারামিটার হিসাবে উপরের পদ্ধতিটি গ্রহণ করে ফলস্বরূপ ফাংশন বা এক্সপ্রেশনটির ফলস্বরূপ সেখানে বাহ্যিক ফাংশন সম্পাদন করার আগে loopপুনরাবৃত্তভাবে কার্যকর করা হয় এবং আমরা কখনই scalaপ্রম্পট ফিরে পাই না ।

scala> def callByValue(x:Int,y:Int) = x
callByValue: (x: Int, y: Int)Int

scala> callByValue(1,loop(1))

0

এটা দেখ:

    object NameVsVal extends App {

  def mul(x: Int, y: => Int) : Int = {
    println("mul")
    x * y
  }
  def add(x: Int, y: Int): Int = {
    println("add")
    x + y
  }
  println(mul(3, add(2, 1)))
}

y: => ইন্ট নামে ডাকা হয়। নামে কল হিসাবে যা পাস হয় তা হ'ল অ্যাড (2, 1)। এটি অলসভাবে মূল্যায়ন করা হবে। সুতরাং কনসোলে আউটপুট "মুল" হবে তার পরে "অ্যাড", যদিও অ্যাডটিকে প্রথমে বলা হবে বলে মনে হচ্ছে। নামে কল করা কোনও ফাংশন পয়েন্টারকে পাশ করার ধরণের কাজ করে।
এখন y থেকে পরিবর্তন করুন: => ইন্টে y: ইনট কনসোল "মুল" এর পরে "অ্যাড" প্রদর্শন করবে! মূল্যায়নের সাধারণ উপায়।


-2

আমি মনে করি না যে এখানে সমস্ত উত্তর সঠিক যুক্তিযুক্ত:

কল করে মান দ্বারা আর্গুমেন্টগুলি একবার গণনা করা হয়:

def f(x : Int, y :Int) = x

// following the substitution model

f(12 + 3, 4 * 11)
f(15, 4194304)
15

আপনি উপরে দেখতে পারেন যে সমস্ত আর্গুমেন্টগুলি মূল্যায়ন করা হয় যেগুলি প্রয়োজন হয় না তা সাধারণভাবে call-by-valueদ্রুত হতে পারে তবে এই ক্ষেত্রে সর্বদা পছন্দ হয় না।

যদি মূল্যায়ন কৌশলটি হত call-by-nameতবে পচনটি হ'ল :

f(12 + 3, 4 * 11)
12 + 3
15

যেমন আপনি উপরে দেখতে পারেন আমাদের কখনই মূল্যায়ন করার দরকার পড়ে না 4 * 11এবং তাই কিছুটা গণনাও সংরক্ষণ করে যা কখনও কখনও উপকারী হতে পারে।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.