জাভা 8 ল্যাম্বডাস, ফাংশন.ভিডেনটিটি () বা টি-> টি


240

আমি ব্যবহার সম্পর্কে একটি প্রশ্ন আছে Function.identity()পদ্ধতিটির ।

নিম্নলিখিত কোডটি কল্পনা করুন:

Arrays.asList("a", "b", "c")
          .stream()
          .map(Function.identity()) // <- This,
          .map(str -> str)          // <- is the same as this.
          .collect(Collectors.toMap(
                       Function.identity(), // <-- And this,
                       str -> str));        // <-- is the same as this.

আপনার (বা বিপরীতে) Function.identity()পরিবর্তে ব্যবহার করার কোনও কারণ আছে কি str->str? আমি মনে করি যে দ্বিতীয় বিকল্পটি আরও পঠনযোগ্য (অবশ্যই স্বাদের বিষয়)। তবে, কারও পছন্দ করা উচিত এমন কোনও "আসল" কারণ আছে?


6
শেষ পর্যন্ত, না, এটি কোনও পার্থক্য করবে না।
fge

50
হয় ঠিক আছে। আপনি যাকে বেশি পঠনযোগ্য মনে করেন তার সাথে যান। (চিন্তা করবেন না, খুশি হন))
ব্রায়ান গোয়েজ

3
আমি বেশি পছন্দ করি t -> tকারণ কেবল এটি পছন্দ করি ।
ডেভিড কনরাড

3
কিছুটা সম্পর্কহীন প্রশ্ন, তবে কেউ কি জানেন যে ভাষা ডিজাইনাররা পরিচয় কেন তৈরি করে () টি টাইপ প্যারামিটারের পরিবর্তে ফাংশনের একটি উদাহরণ দেয় এবং তা ফেরত দেয় যাতে পদ্ধতিটি পদ্ধতির উল্লেখগুলির সাথে ব্যবহার করা যায়?
কিরিল রাখমান

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

উত্তর:


312

বর্তমান জেআরই বাস্তবায়ন হিসাবে, Function.identity()সর্বদা একই নজরে ফিরে আসবে যখন প্রতিটি ঘটনাই identifier -> identifierকেবল নিজস্ব ঘটনা তৈরি করে না, এমনকি একটি পৃথক বাস্তবায়ন শ্রেণিও তৈরি করে। আরও তথ্যের জন্য, এখানে দেখুন

কারণটি হ'ল সংকলকটি সেই ল্যাম্বডা এক্সপ্রেশনটির তুচ্ছ দেহকে ধরে রাখার একটি সিনথেটিক পদ্ধতি উত্পন্ন করে (এর ক্ষেত্রে x->xসমতুল্য return identifier;) এবং রানটাইমকে এই পদ্ধতিটিকে কল করে ফাংশনাল ইন্টারফেসের একটি বাস্তবায়ন তৈরি করতে বলে। সুতরাং রানটাইমটি কেবলমাত্র বিভিন্ন লক্ষ্য পদ্ধতি দেখায় এবং বর্তমান বাস্তবায়ন নির্দিষ্ট পদ্ধতি সমান কিনা তা নির্ধারণের জন্য পদ্ধতিগুলি বিশ্লেষণ করে না।

এর Function.identity()পরিবর্তে ব্যবহার করা x -> xকিছু স্মৃতি সাশ্রয় করতে পারে তবে আপনি যদি সত্যিই x -> xএটির থেকে বেশি পঠনযোগ্য বলে মনে করেন তবে এটি আপনার সিদ্ধান্তটিকে চালিত করবে না Function.identity()

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


5
চমৎকার উত্তর. ডিবাগিং সম্পর্কে আমার কিছু সন্দেহ আছে। কিভাবে এটি দরকারী হতে পারে? x -> xফ্রেম জড়িত ব্যতিক্রম স্ট্যাক ট্রেস পাওয়ার এটি খুব কমই । আপনি কি এই ল্যাম্বডায় ব্রেকপয়েন্টটি সেট করার পরামর্শ দিচ্ছেন? সাধারণত একক বহিঃপ্রকাশের ল্যাম্বডায় (অন্ততপক্ষে
একটিগ্রহের

14
@ তাগীর ভালিভ: আপনি কোডটি ডিবাগ করতে পারেন যা একটি স্বেচ্ছাসেবী ফাংশন গ্রহণ করে এবং সেই ফাংশনের প্রয়োগ পদ্ধতিতে পদক্ষেপ নিতে পারে। তারপরে আপনি ল্যাম্বডা এক্সপ্রেশনটির উত্স কোডে শেষ হতে পারেন। সুস্পষ্ট ল্যাম্বডা এক্সপ্রেশনের ক্ষেত্রে আপনি জানবেন যে ফাংশনটি কোথা থেকে এসেছে এবং কোনও পরিচয় ফাংশন সত্ত্বেও কোন স্থানে সিদ্ধান্তটি পাস করার সিদ্ধান্ত নেওয়ার সুযোগ রয়েছে তা সনাক্ত করার সুযোগ পাবেন। Function.identity()তথ্য ব্যবহার করার সময় হারিয়ে যায়। তারপরে, কল চেইন সাধারণ ক্ষেত্রে সহায়তা করতে পারে তবে চিন্তা করতে পারে, উদাহরণস্বরূপ মাল্টি-থ্রেড মূল্যায়ন যেখানে আসল সূচনাকারী স্ট্যাক ট্রেসে নেই ...
হোলার

2
এই প্রসঙ্গে আকর্ষণীয়: blog.codefx.org/java/inferences-non-capturing-lambdas
উইম দেবলাউ

13
@ উইম দেবলাউউ: আকর্ষণীয়, তবে আমি সর্বদা এটি অন্যভাবে দেখতে পাব: যদি কোনও কারখানা পদ্ধতি তার ডকুমেন্টেশনে স্পষ্টভাবে না জানায় যে এটি প্রতিটি আহ্বানের ক্ষেত্রে নতুন উদাহরণ ফিরে আসবে, আপনি ধরে নিতে পারবেন না যে এটি ঘটবে। সুতরাং এটি না হলে অবাক হওয়ার কিছু নেই। সর্বোপরি, কারখানার পদ্ধতিগুলির পরিবর্তে এটি ব্যবহারের একটি বড় কারণ newnew Foo(…)সঠিক ধরণের একটি নতুন উদাহরণ তৈরির গ্যারান্টি রয়েছে Foo, তবে Foo.getInstance(…)এটির (একটি উপ-প্রকার) বিদ্যমান উপস্থিতি ফিরে আসতে পারে Foo...
হোলগার

93

আপনার উদাহরণে এর মধ্যে কোনও বড় পার্থক্য নেই str -> strএবং Function.identity()যেহেতু অভ্যন্তরীণভাবে এটি সহজ t->t

তবে কখনও কখনও আমরা ব্যবহার করতে পারি না Function.identityকারণ আমরা একটি ব্যবহার করতে পারি না Function। এখানে একবার দেখুন:

List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);

এই জরিমানা সংকলন করা হবে

int[] arrayOK = list.stream().mapToInt(i -> i).toArray();

তবে আপনি যদি সংকলনের চেষ্টা করেন

int[] arrayProblem = list.stream().mapToInt(Function.identity()).toArray();

mapToIntপ্রত্যাশার পরে আপনি সংকলন ত্রুটি পাবেন ToIntFunction, যা সম্পর্কিত নয় Function। এছাড়াও ToIntFunctionনেই identity()পদ্ধতি।


3
দেখুন stackoverflow.com/q/38034982/14731 আরেকটি উদাহরণ যেখানে প্রতিস্থাপন জন্য i -> iসঙ্গে Function.identity()কম্পাইলার ত্রুটি হয়ে যাবে।
গিলি

19
আমি পছন্দ mapToInt(Integer::intValue)
shmosel

4
@ শ্মোসেল এটি ঠিক আছে তবে এটি উল্লেখযোগ্য যে উভয় সমাধান একইরূপে কাজ করবে যেহেতু mapToInt(i -> i)সরলীকরণ mapToInt( (Integer i) -> i.intValue())। আপনি যে সংস্করণটি পরিষ্কার মনে করেন সেগুলি ব্যবহার করুন, আমার mapToInt(i -> i)জন্য এই কোডটির উদ্দেশ্যগুলি আরও ভাল।
Pshemo

1
আমি মনে করি পদ্ধতির উল্লেখগুলি ব্যবহারে পারফরম্যান্স সুবিধা থাকতে পারে তবে এটি বেশিরভাগ ক্ষেত্রে কেবল ব্যক্তিগত পছন্দ। আমি এটিকে আরও বর্ণনামূলক বলে i -> iমনে করি কারণ একটি পরিচয় ফাংশনের মতো দেখায় যা এটি এই ক্ষেত্রে নয়।
shmosel

@ শ্মোসেল আমি পারফরম্যান্স পার্থক্য সম্পর্কে বেশি কিছু বলতে পারি না যাতে আপনি সঠিক হতে পারেন। তবে যদি কর্মক্ষমতা কোনও সমস্যা না হয় তবে আমি সাথেই থাকব i -> iযেহেতু আমার লক্ষ্যটি ইন্টিজার টু ইনট মানচিত্র করা (যা mapToIntবেশ সুন্দরভাবে প্রস্তাব দেয়) সুস্পষ্টভাবে intValue()পদ্ধতিতে কল না করা । এই ম্যাপিংটি কীভাবে অর্জন করা হবে তা সত্যই গুরুত্বপূর্ণ নয়। সুতরাং আসুন কেবল দ্বিমত পোষণ করতে সম্মত হন তবে সম্ভাব্য পারফরম্যান্স পার্থক্যটি নির্দেশ করার জন্য ধন্যবাদ, আমাকে সেদিন আরও ঘনিষ্ঠভাবে নজর দেওয়া দরকার।
Pshemo

44

থেকে JDK উৎস :

static <T> Function<T, T> identity() {
    return t -> t;
}

সুতরাং, না, যতক্ষণ না এটি সিনট্যাক্টিকভাবে সঠিক is


8
আমি ভাবছি যদি এটি ল্যাম্বডা একটি বস্তু তৈরির সম্পর্কিত উপরের উত্তরটিকে অকার্যকর করে - বা যদি এটি কোনও নির্দিষ্ট বাস্তবায়ন হয়।
অরবফিশ

28
@ আরবিফিশ: এটি পুরোপুরি লাইনে আছে। t->tউত্স কোডের প্রতিটি ঘটনাই একটি অবজেক্ট তৈরি করতে পারে এবং এর বাস্তবায়ন Function.identity()হ'ল একটি ঘটনা। অনুরোধকারী সমস্ত কল সাইটগুলি identity()সেই এক বস্তু ভাগ করবে যখন সমস্ত সাইটগুলি স্পষ্টভাবে ল্যাম্বডা এক্সপ্রেশন ব্যবহার করে t->tতাদের নিজস্ব অবজেক্ট তৈরি করবে। পদ্ধতি Function.identity()না যখনই আপনাকে ফ্যাক্টরি পদ্ধতি একটি সাধারণভাবে ব্যবহৃত ল্যামডা অভিব্যক্তি encapsulating তৈরি এবং পরিবর্তে যে পদ্ধতি কল ল্যামডা অভিব্যক্তি পুনরায়, অন্য কোন উপায়ে বিশেষ, আপনি, কিছু স্মৃতি সংরক্ষণ করতে পারেন বর্তমান বাস্তবায়ন দেওয়া
হলগার

আমি অনুমান করছি যে কম্পাইলারটি t->tযখনই পদ্ধতিটি বলা হয় তখনই কোনও নতুন অবজেক্ট তৈরি করা অপ্টিমাইজ করে এবং যখনই পদ্ধতিটি বলা হয় তখন একইটিকে পুনর্ব্যবহার করে?
ড্যানিয়েল গ্রে

1
@ ড্যানিয়েল গ্রে সিদ্ধান্তটি রানটাইম সময়ে নেওয়া হয়। সংকলকটি এমন একটি invokedynamicনির্দেশ সন্নিবেশ করায় যা তথাকথিত বুটস্ট্র্যাপ পদ্ধতিটি চালিয়ে তার প্রথম নির্বাহের সাথে যুক্ত হয়, যা ল্যাম্বডা এক্সপ্রেশনগুলির ক্ষেত্রে অবস্থিত LambdaMetafactory। এই বাস্তবায়ন সিদ্ধান্ত নেয় যে কোনও হ্যান্ডেলটি কোনও নির্মাণকারীর কাছে, কারখানার পদ্ধতিতে বা কোড সর্বদা একই বস্তুটি ফেরত দেয়। এটি ইতিমধ্যে বিদ্যমান হ্যান্ডেলটিতে লিঙ্ক ফেরত দেওয়ার সিদ্ধান্ত নিতে পারে (যা বর্তমানে ঘটে না)।
হোলার

@ হোলজার আপনি কি নিশ্চিত যে এই পরিচয় সনাক্তকরণের ডাকটি অন্তর্ভুক্ত করা সম্ভব না হলে সম্ভাব্য মনোমর্মাইজড হবে (এবং আবার linedোকানো হবে)?
জেসনএন
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.