যে পদ্ধতিটি জাভা 8 তে রূপান্তরিত করে তা হ্রাস করার জন্য কেন একটি সংযুক্তকারী প্রয়োজন


142

combinerস্ট্রিমস reduceপদ্ধতিতে যে ভূমিকা পালন করে তা পুরোপুরি বুঝতে আমার সমস্যা হচ্ছে ।

উদাহরণস্বরূপ, নিম্নলিখিত কোডটি সংকলন করে না:

int length = asList("str1", "str2").stream()
            .reduce(0, (accumulatedInt, str) -> accumulatedInt + str.length());

সংকলন ত্রুটি বলে: (যুক্তি মিলছে না; int java.lang.String রূপান্তর করা যাবে না)

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

int length = asList("str1", "str2").stream()  
    .reduce(0, (accumulatedInt, str ) -> accumulatedInt + str.length(), 
                (accumulatedInt, accumulatedInt2) -> accumulatedInt + accumulatedInt2);

আমি বুঝতে পারি যে সমন্বিত পদ্ধতিটি সমান্তরাল স্ট্রিমগুলিতে ব্যবহৃত হয় - সুতরাং আমার উদাহরণে এটি দুটি মধ্যবর্তী জমে থাকা ints যুক্ত করছে।

তবে আমি বুঝতে পারি না কেন প্রথম উদাহরণটি কম্বিনার ছাড়াই সংকলন করে না বা কম্বিনার কীভাবে স্ট্রিংকে ইনট-এ রূপান্তর সমাধান করছে কেননা এটি কেবল দুটি ইনট একসাথে যুক্ত করছে।

কেউ কি এ বিষয়ে আলোকপাত করতে পারেন?


সংশ্লিষ্ট প্রশ্ন: stackoverflow.com/questions/24202473/...
nosid

2
আহা, এটি সমান্তরাল স্রোতের জন্য ... আমি ফাঁসী বিমূর্ততা বলি!
অ্যান্ডি

উত্তর:


77

আপনি যে দুটি এবং তিনটি আর্গুমেন্ট সংস্করণ reduceব্যবহার করার চেষ্টা করেছিলেন সেগুলির জন্য একই ধরণটি গ্রহণ করে না accumulator

দুটি যুক্তি reduceহিসাবে সংজ্ঞায়িত করা হয় :

T reduce(T identity,
         BinaryOperator<T> accumulator)

আপনার ক্ষেত্রে টি স্ট্রিং, সুতরাং BinaryOperator<T>দুটি স্ট্রিং আর্গুমেন্ট গ্রহণ করা উচিত এবং একটি স্ট্রিং ফিরিয়ে দেওয়া উচিত। তবে আপনি এটিতে একটি int এবং একটি স্ট্রিং পাস করেন, যা আপনার সংকলনের ত্রুটির ফলস্বরূপ - argument mismatch; int cannot be converted to java.lang.String। প্রকৃতপক্ষে, আমি মনে করি যে পরিচয় মান হিসাবে 0 পাস করা এখানেও ভুল, কারণ একটি স্ট্রিং প্রত্যাশিত (টি)।

আরও মনে রাখবেন যে হ্রাস করার এই সংস্করণটি টিএসের একটি প্রবাহকে প্রসেস করে এবং একটি টি প্রদান করে, তাই আপনি স্ট্রিংয়ের স্ট্রিমটিকে কোনও ইনটকে হ্রাস করতে এটি ব্যবহার করতে পারবেন না।

তিনটি যুক্তি reduceহিসাবে সংজ্ঞায়িত করা হয় :

<U> U reduce(U identity,
             BiFunction<U,? super T,U> accumulator,
             BinaryOperator<U> combiner)

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

জন্য BiFunction<U,? super T,U>সঁচায়ক আপনি দুটি ভিন্ন ধরনের (ইউ এবং? সুপার টি), যা আপনার ক্ষেত্রে পূর্ণসংখ্যা এবং স্ট্রিং হয় পরামিতি পাস করতে পারেন। এছাড়াও, পরিচয় মান ইউ আপনার ক্ষেত্রে একটি পূর্ণসংখ্যাকে গ্রহণ করে, সুতরাং এটি 0 প্রদান করা ভাল।

আপনি যা চান তা অর্জনের আর একটি উপায়:

int length = asList("str1", "str2").stream().mapToInt (s -> s.length())
            .reduce(0, (accumulatedInt, len) -> accumulatedInt + len);

এখানে স্ট্রিমের ধরণটি রিটার্নের ধরণের সাথে মেলে reduce, তাই আপনি দুটি প্যারামিটার সংস্করণ ব্যবহার করতে পারেন reduce

অবশ্যই আপনি ব্যবহার করতে হবে না reduce:

int length = asList("str1", "str2").stream().mapToInt (s -> s.length())
            .sum();

8
আপনার শেষ কোডে একটি দ্বিতীয় বিকল্প হিসাবে, আপনি ব্যবহার করতে পারে mapToInt(String::length)ওভার mapToInt(s -> s.length()), না নিশ্চিত যদি এক অপরের উপর ভাল হবে, কিন্তু আমি পাঠযোগ্যতা জন্য সাবেক পছন্দ করে।
স্কিভি

20
কেন combinerপ্রয়োজনীয় প্রয়োজন কেন তা না পেয়ে অনেকে উত্তর পেয়ে যাবেন কেন accumulatorযথেষ্ট পরিমাণে না থাকা । সেক্ষেত্রে: থ্রেডগুলির "সঞ্চিত" ফলাফলগুলি একত্রিত করতে কেবল সমান্তরাল স্ট্রিমগুলির জন্য কম্বিনার প্রয়োজন।
ddekany

1
আমি আপনার উত্তরটি বিশেষভাবে দরকারী বলে মনে করি না - কারণ আপনি সংযুক্তকারীটি কী করা উচিত এবং আমি কীভাবে এটি ছাড়া কাজ করতে পারি তা মোটেই ব্যাখ্যা করেন না! আমার ক্ষেত্রে আমি টাইপ টি একটি ইউ তে হ্রাস করতে চাই তবে এটি কখনও সমান্তরালভাবে করার কোনও উপায় নেই। এটি সহজভাবে সম্ভব নয়। যে সিস্টেমটি আমি চাই না / তার সমান্তরালতা প্রয়োজন এবং কীভাবে এটি সংযোজকটি ছেড়ে দেয় তা আপনি কীভাবে বলতে পারেন?
জোর্ডিড

@ জর্ডিড স্ট্রিমস এপিআইতে কম্বিনার পাস না করে টি-তে টাইপ টি হ্রাস করার বিকল্প অন্তর্ভুক্ত নয়।
ইরান

216

Eran এর উত্তর দুই ARG এবং তিন ARG এর সংস্করণের মধ্যে পার্থক্য বর্ণনা reduceযে সাবেক হ্রাস Stream<T>করার Tযেহেতু আধুনিক হ্রাস Stream<T>করার U। যাইহোক, এটি হ্রাস Stream<T>করার সময় অতিরিক্ত সংযুক্তকারী ফাংশনের প্রয়োজনীয়তাটি আসলে ব্যাখ্যা করে না U

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

আসুন প্রথমে হ্রাসের দ্বি-আরগ সংস্করণটি বিবেচনা করুন:

T reduce(I, (T, T) -> T)

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

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

এখন একটি প্রকল্পিত দুই ARG হ্রাস অপারেশন যে হ্রাস বিবেচনা করা যাক Stream<T>করতে U। অন্যান্য ভাষায়, এটিকে "ভাঁজ" বা "ভাঁজ-বাম" অপারেশন বলা হয় তাই আমি এখানে এটি ডাকব। নোট করুন এটি জাভাতে নেই।

U foldLeft(I, (U, T) -> U)

(মনে রাখবেন যে পরিচয় মানটি Iইউ টাইপের)

এর ক্রমিক সংস্করণটি foldLeftঠিক এর ক্রমিক সংস্করণটির মতো যা reduceঅন্তর্বর্তী মানগুলি টাইপ টির পরিবর্তে ইউ টাইপের হয় তবে এটি অন্যথায় একই রকম। (একটি অনুমানমূলক foldRightঅপারেশন একই রকম হবে ব্যতীত অপারেশনগুলি বাম-থেকে-ডান পরিবর্তে ডান থেকে বামে সঞ্চালিত হবে))

এখন এর সমান্তরাল সংস্করণ বিবেচনা করুন foldLeft। স্ট্রিমটিকে বিভাগগুলিতে বিভক্ত করে শুরু করা যাক। তারপরে আমরা প্রতিটি এন থ্রেড এর বিভাগে টি মানগুলি কম করে ইউ টাইপের এন মধ্যবর্তী মানগুলিতে পরিণত করতে পারি Now এখন কি? আমরা টাইপ ইউ এর এন মানগুলি থেকে কীভাবে ইউ টাইপের একক ফলাফল পেতে পারি?

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

U reduce(I, (U, T) -> U, (U, U) -> U)

বা, জাভা সিনট্যাক্স ব্যবহার করে:

<U> U reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator<U> combiner)

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

পরিশেষে, জাভা সরবরাহ করে না foldLeftএবং foldRightঅপারেশন করে না কারণ তারা বোঝায় যে ক্রিয়াকলাপগুলি সহজাত ক্রমযুক্ত operations ক্রমগত এবং সমান্তরাল ক্রিয়াকলাপকে সমানভাবে সমর্থন করে এমন APIs সরবরাহ করার উপরে উপরে বর্ণিত নকশার নীতিটির সাথে এই সংঘর্ষ।


7
সুতরাং আপনার যদি প্রয়োজন হয় তবে আপনি কী করতে পারেন foldLeftকারণ গণনাটি আগের ফলাফলের উপর নির্ভর করে এবং সমান্তরাল করা যায় না?
অ্যামিবে

5
@ আমোবে আপনি নিজের ফোল্ডফেল্ট ব্যবহার করে প্রয়োগ করতে পারেন forEachOrdered। মধ্যবর্তী রাষ্ট্রটি ক্যাপচারড ভেরিয়েবলে রাখতে হবে, যদিও।
স্টুয়ার্ট

স্টুয়ার্টমার্কস ধন্যবাদ, আমি jOOλ ব্যবহার করে শেষ করেছি λ তারা একটি ঝরঝরে বাস্তবায়ন আছেfoldLeft
অ্যামিবে 10'15

1
এই উত্তর ভালবাসা! আমি ভুল হলে আমাকে সংশোধন করুন: এটি ব্যাখ্যা করে যে ওপি-র চলমান উদাহরণ (দ্বিতীয়টি) কখন সংবাহককে চালিত করবে না, যখন চালানো হবে, প্রবাহের অনুক্রমিক being
লুইজি কর্টিজ

2
এটি প্রায় সমস্ত কিছু ব্যাখ্যা করে ... ব্যতীত: কেন এটি ক্রমানুসারে ভিত্তিক হ্রাস বাদ দেওয়া উচিত। আমার ক্ষেত্রে এটি সমান্তরালভাবে করা অসম্ভব কারণ আমার হ্রাস তার পূর্বসূরিদের ফলাফলের মধ্যবর্তী ফলাফলের প্রতিটি ফাংশনকে কল করে একটি ইউ-তে ফাংশনের একটি তালিকা হ্রাস করে। এটি একেবারে সমান্তরালে করা যায় না এবং একটি সংযুক্তকারী বর্ণনা করার কোনও উপায় নেই। এটি সম্পাদন করতে আমি কোন পদ্ধতিটি ব্যবহার করতে পারি?
জোর্ডিড

116

যেহেতু ধারণাগুলি স্পষ্ট করতে আমি ডুডলস এবং তীর পছন্দ করি ... আসুন শুরু করা যাক!

স্ট্রিং থেকে স্ট্রিং (অনুক্রমিক স্ট্রিম)

মনে করুন 4 টি স্ট্রিং রয়েছে: আপনার লক্ষ্য এই জাতীয় স্ট্রিংগুলিকে একটিতে সংযুক্ত করা। আপনি মূলত একটি টাইপ দিয়ে শুরু করুন এবং একই ধরণের সাথে শেষ করুন।

আপনি এটি দিয়ে এটি অর্জন করতে পারেন

String res = Arrays.asList("one", "two","three","four")
        .stream()
        .reduce("",
                (accumulatedStr, str) -> accumulatedStr + str);  //accumulator

এবং এটি আপনাকে কী ঘটছে তা কল্পনা করতে সহায়তা করে:

এখানে চিত্র বর্ণনা লিখুন

সংযোজক ফাংশন ধাপে ধাপে আপনার (লাল) প্রবাহের উপাদানগুলি চূড়ান্ত হ্রাস (সবুজ) মানকে রূপান্তর করে। সঞ্চয়ের ফাংশন কেবল কোনও Stringবস্তুকে অন্যটিতে রূপান্তরিত করে String

স্ট্রিং থেকে ইনট পর্যন্ত (সমান্তরাল স্ট্রিম)

মনে করুন একই 4 টি স্ট্রিং রয়েছে: আপনার নতুন লক্ষ্যটি তাদের দৈর্ঘ্যের যোগফল এবং আপনি আপনার স্ট্রিমটিকে সমান্তরাল করতে চান।

আপনার যা প্রয়োজন তা হ'ল এইরকম:

int length = Arrays.asList("one", "two","three","four")
        .parallelStream()
        .reduce(0,
                (accumulatedInt, str) -> accumulatedInt + str.length(),                 //accumulator
                (accumulatedInt, accumulatedInt2) -> accumulatedInt + accumulatedInt2); //combiner

এবং এটি যা ঘটছে তার একটি পরিকল্পনা

এখানে চিত্র বর্ণনা লিখুন

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

স্ট্রিং থেকে ইনট পর্যন্ত (ক্রমিক ধারা)

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


7
এর জন্য ধন্যবাদ. আমারও পড়ার দরকার পড়েনি। আমি আশা করি তারা কেবল একটি ফ্রেইকিং ফোল্ড ফাংশন যুক্ত করেছে।
লোডেভিজক বোগার্ডস

1
@ লোডউইজকবোগার্ডস এটির সাহায্যে আনন্দিত! এখানে জাভাডোকটি সত্যই বেশ ক্রিপ্টিক
লুজি কর্টিজ

@ লুইজি কর্টিজ সমান্তরাল প্রবাহে এটি কি সবসময় উপাদানগুলিকে জোড়ায় ভাগ করে দেয়?
TheLogicGuy

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

আটটি স্ট্রিং দিয়ে কমিয়ে চিত্রিত করা আরও অনেক ভাল হবে ...
একেতেরিনা ইভানোভা আইসজা.नेट

0

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

list.stream().reduce(identity,
                     accumulator,
                     combiner);

হিসাবে একই ফলাফল উত্পাদন:

list.stream().map(i -> accumulator(identity, i))
             .reduce(identity,
                     combiner);

এই জাতীয় mapকৌশল নির্দিষ্ট উপর নির্ভর করে accumulatorএবং combinerজিনিসগুলি বেশ ধীর করে দিতে পারে slow
তাগীর ভালেভ

বা, এটিকে তাত্পর্যপূর্ণ করে দিন যেহেতু আপনি এখন accumulatorপ্রথম প্যারামিটারটি বাদ দিয়ে সহজ করতে পারবেন ।
ক্যুইজ 123

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