কোটলিনে কখন একটি ইনলাইন ফাংশন ব্যবহার করবেন?


105

আমি জানি যে একটি ইনলাইন ফাংশন সম্ভবত কার্যকারিতা উন্নত করবে এবং উত্পন্ন কোডটি বাড়িয়ে তুলবে, তবে কখন এটি ব্যবহার করা ঠিক হবে তা নিশ্চিত আমি নই।

lock(l) { foo() }

প্যারামিটারের জন্য একটি ফাংশন অবজেক্ট তৈরি এবং কল উত্পন্ন করার পরিবর্তে সংকলকটি নিম্নলিখিত কোডটি নির্গত করতে পারে। ( উত্স )

l.lock()
try {
  foo()
}
finally {
  l.unlock()
}

তবে আমি দেখতে পেয়েছি যে কোনও ইন-ইনলাইন ফাংশনের জন্য কোটলিন দ্বারা তৈরি কোনও ফাংশন অবজেক্ট নেই। কেন?

/**non-inline function**/
fun lock(lock: Lock, block: () -> Unit) {
    lock.lock();
    try {
        block();
    } finally {
        lock.unlock();
    }
}

7
এর জন্য দুটি প্রধান ব্যবহারের কেস রয়েছে, একটি হ'ল নির্দিষ্ট ধরণের উচ্চতর অর্ডার ফাংশন সহ এবং অন্যটি রিফাইড টাইপ পরামিতি। ইনলাইন ফাংশনগুলির ডকুমেন্টেশন এগুলি অন্তর্ভুক্ত করে: kotlinlang.org/docs/references/inline-funifications.html
zsmb13

2
@ zsmb13 ধন্যবাদ, স্যার। তবে আমি তা বুঝতে পারি না: "প্যারামিটারের জন্য একটি ফাংশন অবজেক্ট তৈরি এবং কল উত্পন্ন করার পরিবর্তে
সংকলকটি

2
টিবিএইচ উদাহরণটি আমি পাই না।
ফিলিটি_উইজার্ড

উত্তর:


279

ধরা যাক আপনি একটি উচ্চতর অর্ডার ফাংশন তৈরি করেন যা একটি ল্যাম্বডা টাইপের () -> Unit(কোনও প্যারামিটার নয়, কোনও ফেরতের মান নয়) নেয় এবং এটি এর মতো কার্যকর করে:

fun nonInlined(block: () -> Unit) {
    println("before")
    block()
    println("after")
}

জাভা সংসদে, এটি এরকম কিছুতে অনুবাদ করা হবে (সরলীকৃত!):

public void nonInlined(Function block) {
    System.out.println("before");
    block.invoke();
    System.out.println("after");
}

এবং আপনি যখন কোটলিন থেকে কল করবেন ...

nonInlined {
    println("do something here")
}

হুডের নীচে, এখানে একটি উদাহরণ তৈরি করা Functionহবে, যা ল্যাম্বদার অভ্যন্তরে কোডটি জড়িয়ে রাখবে (আবার, এটি সরলীকৃত):

nonInlined(new Function() {
    @Override
    public void invoke() {
        System.out.println("do something here");
    }
});

সুতরাং মূলত, এই ফাংশনটি কল করা এবং এতে একটি ল্যাম্বডা পাস করা সর্বদা কোনও Functionবস্তুর উদাহরণ তৈরি করবে ।


অন্যদিকে, আপনি inlineকীওয়ার্ডটি ব্যবহার করলে :

inline fun inlined(block: () -> Unit) {
    println("before")
    block()
    println("after")
}

আপনি যখন এটিকে কল করবেন:

inlined {
    println("do something here")
}

কোনও Functionউদাহরণ তৈরি করা হবে না, পরিবর্তে, blockইনলাইনড ফাংশনটির অভ্যন্তরের অনুরোধের চারপাশের কোডটি কল সাইটে অনুলিপি করা হবে, তাই আপনি বাইটকোডে এই জাতীয় কিছু পাবেন:

System.out.println("before");
System.out.println("do something here");
System.out.println("after");

এই ক্ষেত্রে, কোনও নতুন দৃষ্টান্ত তৈরি করা হয় না।


19
প্রথম স্থানে ফাংশন অবজেক্ট র‌্যাপার থাকার সুবিধা কী? অর্থাত্ - কেন সব কিছু অন্তর্ভুক্ত নয়?
আর্টুর ভ্যানকান্স

14
এই পদ্ধতিতে আপনি যথেচ্ছভাবে প্যারামিটার হিসাবে চারপাশে ফাংশনগুলি পাস করতে পারেন, এগুলি ভেরিয়েবলে সংরক্ষণ করতে পারেন ইত্যাদি
zsmb13

6
@ Zsmb13
Yajairo87

2
আপনি তা করতে পারেন এবং আপনি যদি তাদের সাথে জটিল জিনিসগুলি করেন তবে শেষ পর্যন্ত আপনি কীওয়ার্ড noinlineএবং crossinlineকীওয়ার্ডগুলি সম্পর্কে জানতে চান - দস্তাবেজগুলি দেখুন
zsmb13

2
ডক্সটি
CorayThan

43

আমাকে যুক্ত করতে দিন: "কখন ব্যবহার করবেন না inline" :

1) আপনার যদি এমন একটি সাধারণ ফাংশন থাকে যা অন্যান্য কার্যগুলি আর্গুমেন্ট হিসাবে গ্রহণ করে না, সেগুলি ইনলাইন করা কোনও অর্থবোধ করে না। ইন্টেলিজ আপনাকে সতর্ক করবে:

ইনলাইনিং '...' এর প্রত্যাশিত পারফরম্যান্স প্রভাব তুচ্ছ। ইনলাইনিং কার্যকরী ধরণের পরামিতিগুলির সাথে ফাংশনগুলির জন্য সর্বোত্তম কাজ করে

2) আপনার "ফাংশনাল ধরণের পরামিতিগুলির সাথে" একটি ফাংশন থাকলেও, আপনি ইনাইলাইনিং কাজ করে না এমনটি জানিয়ে সংকলকটির মুখোমুখি হতে পারেন। এই উদাহরণ বিবেচনা করুন:

inline fun calculateNoInline(param: Int, operation: IntMapper): Int {
    val o = operation //compiler does not like this
    return o(param)
}

এই কোডটি ত্রুটির সাথে সংকলন করবে না:

'...' তে ইনলাইন-প্যারামিটারের অবৈধ ব্যবহার। পরামিতি ঘোষণায় 'নোইনলাইন' পরিবর্তনকারী যুক্ত করুন।

কারণটি হ'ল সংকলক এই কোডটি ইনলাইন করতে অক্ষম। যদি operationকোনও বস্তুতে আবৃত না থাকে (যা বোঝায় যে inlineআপনি এড়াতে চান) তবে কীভাবে এটি একটি ভেরিয়েবলের জন্য নির্ধারিত হতে পারে? এই ক্ষেত্রে, সংকলকটি যুক্তি তৈরির পরামর্শ দেয় noinline। একটি হচ্ছে inlineএকটি একক সঙ্গে ফাংশন noinlineফাংশন কোন অর্থে দেখা যায় না, যে করবেন না। তবে, যদি কার্যকরী ধরণের একাধিক পরামিতি থাকে তবে প্রয়োজনে সেগুলির কয়েকটিকে ইনলাইনিংয়ের বিষয়ে বিবেচনা করুন।

সুতরাং এখানে কিছু প্রস্তাবিত বিধি দেওয়া হয়েছে:

  • আপনি করতে পারেন ইনলাইন যখন সমস্ত ক্রিয়ামূলক টাইপ পরামিতি সরাসরি বলা হয় বা পাস অন্যান্য ইনলাইন ফাংশন
  • আপনি উচিত ইনলাইন যখন ^ ক্ষেত্রে দেখা যায়।
  • ফাংশনটির ভিতরে কোনও ভেরিয়েবলের জন্য ফাংশন প্যারামিটার বরাদ্দ করা হলে আপনি ইনলাইন করতে পারবেন না
  • আপনার কমপক্ষে কোনও কার্যকরী ধরণের প্যারামিটারগুলি ইনলাইন করা যেতে পারে, অন্যদের জন্য ব্যবহার করুন কিনা তা আপনাকে ইনলাইনিংয়ের বিষয়টি বিবেচনা করা উচিতnoinline
  • আপনার বিশাল ফাংশনগুলি ইনলাইন করা উচিত নয় , উত্পন্ন বাইট কোড সম্পর্কে ভাবেন। এটি ফাংশনটি যে সমস্ত জায়গায় ডেকে আনা হয়েছে সেগুলিতে এটি অনুলিপি করা হবে।
  • আর একটি ব্যবহারের ক্ষেত্রে reifiedটাইপ পরামিতি, যা আপনাকে ব্যবহার করা প্রয়োজন inlineএখানে পড়ুন ।

4
প্রযুক্তিগতভাবে আপনি এখনও ফাংশনগুলি ইনলাইন করতে পারেন যা ল্যাম্বডা এক্সপ্রেশনগুলি সঠিকভাবে গ্রহণ করে না? .. এখানে সুবিধাটি হ'ল সেই ক্ষেত্রে ফাংশন কল ওভারহেড এড়ানো হয় .. স্কালার মতো ভাষা এটিকে অনুমতি দেয় .. নিশ্চিত নয় কেন কোটলিন এই ধরণের ইনলাইন- ing
দুর্বৃত্ত এক

3
@ দুর্বৃত্ত ওয়ান কোটলিন এই বার ইনলাইনিং করতে নিষেধ করে না। ভাষা লেখকরা কেবল দাবি করছেন যে পারফরম্যান্স সুবিধাটি তুচ্ছ বলে মনে হচ্ছে। ছোট পদ্ধতিগুলি ইতিমধ্যে জেআইটি অপ্টিমাইজেশনের সময় জেভিএম দ্বারা অন্তর্ভুক্ত হওয়ার সম্ভাবনা রয়েছে, বিশেষত যদি তারা প্রায়শই মৃত্যুদন্ড কার্যকর করা হয়। আর একটি ক্ষেত্রে যেখানে inlineক্ষতিকারক হতে পারে সেগুলি হ'ল যখন ফাংশনাল প্যারামিটারগুলি ইনলাইন ফাংশনে একাধিকবার বলা হয়, যেমন বিভিন্ন শর্তাধীন শাখায়। আমি কেবল একটি মামলায় দৌড়েছি যেখানে ক্রিয়ামূলক যুক্তিগুলির জন্য বাইটকোডের সমস্তগুলিই এর কারণে নকল হয়ে গিয়েছিল।
মাইক হিল

5

যখন আমরা ইনলাইন পরিবর্তনকারীটি ব্যবহার করি তখন সবচেয়ে গুরুত্বপূর্ণ ক্ষেত্রে হ'ল আমরা যখন প্যারামিটার ফাংশনগুলির সাথে ব্যবহারের মতো ফাংশনগুলি সংজ্ঞায়িত করি। সংগ্রহ বা স্ট্রিং প্রসেসিং (যেমন filter, mapবা joinToString) বা কেবল স্বতন্ত্র ফাংশনগুলি একটি নিখুঁত উদাহরণ।

এই কারণেই ইনলাইন পরিবর্তনকারী সাধারণত গ্রন্থাগার বিকাশকারীদের জন্য একটি গুরুত্বপূর্ণ অপটিমাইজেশন। এটি কীভাবে কাজ করে এবং এর উন্নতি এবং ব্যয়গুলি কী তা তাদের জানা উচিত। যখন আমরা ফাংশনের ধরণের পরামিতিগুলি সহ আমাদের নিজস্ব ফাংশনগুলি সংজ্ঞায়িত করি তখন আমাদের প্রকল্পগুলিতে ইনলাইন পরিবর্তনকারী ব্যবহার করা উচিত।

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

এছাড়াও, একটি কোড আকার সমস্যা আছে। একটি বৃহত ফাংশন ইনলাইন করা নাটকীয়ভাবে বাইটকোডের আকার বাড়িয়ে তুলতে পারে কারণ এটি প্রতিটি কল সাইটে অনুলিপি করা হয়। এই ধরনের ক্ষেত্রে, আপনি ফাংশনটি রিফেক্টর করতে পারেন এবং নিয়মিত ফাংশনে কোড এক্সট্রাক্ট করতে পারেন।


4

উচ্চ-অর্ডার ফাংশনগুলি খুব সহায়ক এবং তারা reusabilityকোডের সত্যই উন্নতি করতে পারে । তবে এগুলি ব্যবহারের ক্ষেত্রে সবচেয়ে বড় উদ্বেগ হ'ল দক্ষতা। লাম্বদা এক্সপ্রেশন ক্লাসে সংকলিত হয় (প্রায়শই বেনামে ক্লাস হয়), এবং জাভাতে অবজেক্ট তৈরি করা একটি ভারী ক্রিয়াকলাপ। আমরা তবুও উচ্চতর অর্ডার ফাংশনগুলি কার্যকর উপায়ে ব্যবহার করতে পারি, সমস্ত সুবিধা বজায় রেখে ফাংশনগুলিকে ইনলাইন করে।

এখানে ছবিতে ইনলাইন ফাংশন আসে

যখন কোনও ফাংশন হিসাবে চিহ্নিত করা হয় inline, কোড সংকলনের সময় সংকলক ফাংশনের আসল শরীরের সাথে সমস্ত ফাংশন কলগুলি প্রতিস্থাপন করবে। এছাড়াও, আর্গুমেন্ট হিসাবে প্রদত্ত ল্যাম্বদা এক্সপ্রেশনগুলি তাদের আসল শরীরের সাথে প্রতিস্থাপন করা হয়। তাদের ফাংশন হিসাবে বিবেচনা করা হবে না, তবে আসল কোড হিসাবে।

সংক্ষেপে: - ইনলাইন -> কল করার পরিবর্তে, তারা সংকলন সময়ে ফাংশনের বডি কোড দ্বারা প্রতিস্থাপিত হয় ...

কোটলিনে, অন্য ফাংশনের প্যারামিটার হিসাবে কোনও ফাংশন ব্যবহার করা (যা উচ্চতর অর্ডার ফাংশন হিসাবে পরিচিত) জাভা অপেক্ষা প্রাকৃতিক বোধ করে।

যদিও ল্যাম্বডাস ব্যবহারের কিছু অসুবিধা রয়েছে। যেহেতু তারা বেনাম শ্রেণি (এবং তাই বস্তু), তাই তাদের মেমরির প্রয়োজন (এবং এমনকি আপনার অ্যাপ্লিকেশনের সামগ্রিক পদ্ধতিতে এটি যোগ করতে পারে)। এড়াতে, আমরা আমাদের পদ্ধতিগুলি ইনলাইন করতে পারি।

fun notInlined(getString: () -> String?) = println(getString())

inline fun inlined(getString: () -> String?) = println(getString())

উপরের উদাহরণ থেকে : - এই দুটি ফাংশন ঠিক একই কাজ করে - getString ফাংশনের ফলাফল মুদ্রণ করে। একটি ইনলাইনড এবং অন্যটি নয়।

আপনি যদি পচা জাভা কোডটি পরীক্ষা করে দেখেন তবে দেখতে পাবেন যে পদ্ধতিগুলি সম্পূর্ণ অভিন্ন। কারণ ইনলাইন কীওয়ার্ডটি কল-সাইটে কোডটি অনুলিপি করার জন্য কম্পাইলারের নির্দেশ inst

তবে, যদি আমরা নীচের মতো অন্য ফাংশনে কোনও ফাংশনের ধরণটি পাস করি:

//Compile time error… Illegal usage of inline function type ftOne...
 inline fun Int.doSomething(y: Int, ftOne: Int.(Int) -> Int, ftTwo: (Int) -> Int) {
    //passing a function type to another function
    val funOne = someFunction(ftOne)
    /*...*/
 }

এটি সমাধানের জন্য, আমরা নীচে হিসাবে আমাদের ফাংশনটি আবার লিখতে পারি:

inline fun Int.doSomething(y: Int, noinline ftOne: Int.(Int) -> Int, ftTwo: (Int) -> Int) {
    //passing a function type to another function
    val funOne = someFunction(ftOne)
    /*...*/}

ধরুন আমাদের নীচের মতো উচ্চতর অর্ডার ফাংশন রয়েছে:

inline fun Int.doSomething(y: Int, noinline ftOne: Int.(Int) -> Int) {
    //passing a function type to another function
    val funOne = someFunction(ftOne)
    /*...*/}

এখানে, সংকলকটি যখন কেবল একটি ল্যাম্বডা প্যারামিটার থাকে এবং আমরা এটিকে অন্য ফাংশনে পৌঁছে দিচ্ছি তখন ইনলাইন কীওয়ার্ডটি ব্যবহার না করতে আমাদের বলবে। সুতরাং, আমরা নীচের মত উপরের ফাংশনটি আবার লিখতে পারি:

fun Int.doSomething(y: Int, ftOne: Int.(Int) -> Int) {
    //passing a function type to another function
    val funOne = someFunction(ftOne)
    /*...*/
}

দ্রষ্টব্য : -আমাদের পাশাপাশি কীওয়ার্ড নাইনলাইনও সরিয়ে ফেলতে হয়েছিল কারণ এটি কেবল ইনলাইন ফাংশনগুলির জন্যই ব্যবহার করা যেতে পারে!

মনে করুন আমাদের এর মতো ফাংশন রয়েছে ->

fun intercept() {
    // ...
    val start = SystemClock.elapsedRealtime()
    val result = doSomethingWeWantToMeasure()
    val duration = SystemClock.elapsedRealtime() - start
    log(duration)
    // ...}

এটি দুর্দান্ত কাজ করে তবে পরিমাপের কোডটির সাথে ফাংশনের যুক্তির মাংস দূষিত হয় যা আপনার সহকর্মীদের জন্য যা চলছে তা কাজ করা আরও শক্ত করে তোলে। :)

একটি ইনলাইন ফাংশন কীভাবে এই কোডটিকে সহায়তা করতে পারে তা এখানে:

      fun intercept() {
    // ...
    val result = measure { doSomethingWeWantToMeasure() }
    // ...
    }

     inline fun <T> measure(action: () -> T) {
    val start = SystemClock.elapsedRealtime()
    val result = action()
    val duration = SystemClock.elapsedRealtime() - start
    log(duration)
    return result
    }

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

ইনলাইন আপনাকে ল্যাম্বডা পরিমাপের মতো (মাইল্যামদা) পাস করার পরিবর্তে একটি বন্ধের (la ...}) মধ্যে ল্যাম্বডা যুক্তি দিয়ে একটি ফাংশন কল করতে দেয়


2

আপনি যখন সাসপেন্ড ব্লকটি ব্যবহার করে এমন একটি ফাংশন তৈরি করেন তখন একটি সাধারণ ক্ষেত্রে আপনি যখন এটি চান তা হতে পারে। এই বিবেচনা.

fun timer(block: () -> Unit) {
    // stuff
    block()
    //stuff
}

fun logic() { }

suspend fun asyncLogic() { }

fun main() {
    timer { logic() }

    // This is an error
    timer { asyncLogic() }
}

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

suspend fun timer(block: suspend () -> Unit) {
    // stuff
    block()
    // stuff
}

তবে তারপরে এটি কেবল কর্টাইন / স্থগিত ফাংশন থেকেই ব্যবহার করা যায়। তারপরে আপনি এ্যাসিঙ্ক সংস্করণ এবং এই ব্যবহারগুলির একটি অ-এসিঙ্ক সংস্করণ তৈরি শেষ করবেন। আপনি যদি ইনলাইন তৈরি করেন তবে সমস্যাটি চলে যায়।

inline fun timer(block: () -> Unit) {
    // stuff
    block()
    // stuff
}

fun main() {
    // timer can be used from anywhere now
    timer { logic() }

    launch {
        timer { asyncLogic() }
    }
}

এখানে ত্রুটি অবস্থার সাথে একটি কোটলিন খেলার মাঠ । এটি সমাধানের জন্য টাইমারকে ইনলাইন করুন।

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