একাধিক মিলের টার্গেট ধরণের সাথে ল্যাম্বডা এক্সপ্রেশনের জন্য পদ্ধতি স্বাক্ষর নির্বাচন


11

আমি একটি প্রশ্নের উত্তর দিচ্ছিলাম এবং এমন একটি দৃশ্যে ছুটেছিলাম যা আমি ব্যাখ্যা করতে পারি না। এই কোডটি বিবেচনা করুন:

interface ConsumerOne<T> {
    void accept(T a);
}

interface CustomIterable<T> extends Iterable<T> {
    void forEach(ConsumerOne<? super T> c); //overload
}

class A {
    private static CustomIterable<A> iterable;
    private static List<A> aList;

    public static void main(String[] args) {
        iterable.forEach(a -> aList.add(a));     //ambiguous
        iterable.forEach(aList::add);            //ambiguous

        iterable.forEach((A a) -> aList.add(a)); //OK
    }
}

আমি বুঝতে পারছি না কেন ল্যাম্বডা এর পরামিতি স্পষ্টভাবে টাইপ (A a) -> aList.add(a)করে কোডটি সংকলন করে। অতিরিক্তভাবে, কেন এটি ওভারলোডের সাথে Iterableযুক্তটির চেয়ে লিঙ্কযুক্ত CustomIterable?
এর কিছু ব্যাখ্যা আছে বা অনুমানের সম্পর্কিত বিভাগের লিঙ্ক আছে কি?

দ্রষ্টব্য: প্রসারিত iterable.forEach((A a) -> aList.add(a));হলে কেবল সংকলন ( অস্পষ্ট ত্রুটির ফলস্বরূপ পদ্ধতিগুলি ওভারলোডিংকে পরিষ্কারভাবে )CustomIterable<T>Iterable<T>CustomIterable


উভয় এ অর্জন করা:

  • ওপেনজেডক সংস্করণ "১৩.০.২" 2020-01-14 Elpipse
    compiler
  • ওপেনজেডক সংস্করণ "1.8.0_232"
    ইলিপস সংকলক

সম্পাদনা : উপরের কোডটি মেভেনের সাথে বিল্ডিংয়ে সংকলন করতে ব্যর্থ হয়েছে যখন Eclipse কোডটির শেষ লাইনটিকে সফলভাবে সংকলন করেছে।


3
জাভা ৮ এ তিনটির কোনওটিই সংকলন করেন নি। এখন আমি নিশ্চিত নই যে এটি কোনও বাগ যা নতুন সংস্করণে স্থির হয়েছে, বা প্রবর্তিত একটি বাগ / বৈশিষ্ট্য ... আপনার সম্ভবত জাভা সংস্করণ নির্দিষ্ট করা উচিত
সুইপার

@ সুইপার আমি প্রথম দিকে jdk-13 ব্যবহার করে এটি পেয়েছি। জাভা 8 (jdk8u232) এর পরবর্তী পরীক্ষাগুলি একই ত্রুটিগুলি দেখায়। নিশ্চিত না কেন শেষটি কোনওটি আপনার মেশিনে সংকলিত হয় না।
ernest_k

দুটি অনলাইন সংকলককে পুনরুত্পাদন করা যায় না ( 1 , 2 )। আমি আমার মেশিনে 1.8.0_221 ব্যবহার করছি। এটি ওয়েয়ার্ড এবং ওয়েডার হয়ে যাচ্ছে ...
সুইপার

1
@ernest_k Eclipse এর নিজস্ব সংকলক বাস্তবায়ন রয়েছে। এটি প্রশ্নের গুরুত্বপূর্ণ তথ্য হতে পারে। এছাড়াও, পরিষ্কার ক্লাবের নির্মাণের ত্রুটিগুলিও শেষ পংক্তিকে আমার মতে প্রশ্নটিতে তুলে ধরা উচিত। অন্যদিকে, লিঙ্কযুক্ত প্রশ্ন সম্পর্কে, ধারণাটি ওপিও গ্রহনটি ব্যবহার করছে তা পরিষ্কার করা যেতে পারে, যেহেতু কোডটি পুনরায় উত্পাদনযোগ্য নয়।
নামান

2
যদিও আমি বুঝতে পারি যে প্রশ্নের বৌদ্ধিক মূল্য, আমি কেবল ওভারলোডেড পদ্ধতি তৈরির বিরুদ্ধে কেবল পরামর্শ দিতে পারি যা কেবলমাত্র কার্যকরী ইন্টারফেসের মধ্যে পৃথক এবং আশা করি যে এটি নিরাপদে একটি ল্যাম্বডা পাসিং বলা যেতে পারে। আমি বিশ্বাস করি না যে ল্যাম্বদা টাইপের অনুমিতি এবং ওভারলোডিংয়ের সংমিশ্রণ এমন একটি বিষয় যা গড় প্রোগ্রামার কখনও কখনও বোঝার কাছাকাছি চলে আসবে। এটি অনেকগুলি ভেরিয়েবলগুলির সাথে সমীকরণ যা ব্যবহারকারীরা নিয়ন্ত্রণ করে না। এড করুন, দয়া করে :)
স্টিফান হার্মান

উত্তর:


8

টিএল; ডিআর, এটি একটি সংকলক বাগ।

উত্তরাধিকার সূত্রে প্রাপ্ত বা কোনও ডিফল্ট পদ্ধতিতে কোনও প্রয়োগযোগ্য পদ্ধতিতে অগ্রাধিকার দেওয়ার কোনও নিয়ম নেই। মজার বিষয় হল, যখন আমি কোডটি পরিবর্তন করি

interface ConsumerOne<T> {
    void accept(T a);
}
interface ConsumerTwo<T> {
  void accept(T a);
}

interface CustomIterable<T> extends Iterable<T> {
    void forEach(ConsumerOne<? super T> c); //overload
    void forEach(ConsumerTwo<? super T> c); //another overload
}

iterable.forEach((A a) -> aList.add(a));বিবৃতি অন্ধকার একটি ত্রুটি উৎপন্ন হয়।

যেহেতু কোন সম্পত্তি forEach(Consumer<? super T) c)Iterable<T> ইন্টারফেস থেকে পদ্ধতির অন্য ওভারলোড ঘোষণার সময় পরিবর্তিত হয়নি, তাই Eclipse এর এই পদ্ধতিটি নির্বাচন করার সিদ্ধান্ত পদ্ধতির কোনও সম্পত্তির উপর ভিত্তি করে (ধারাবাহিকভাবে) হতে পারে না। এটি এখনও একমাত্র উত্তরাধিকার সূত্রে প্রাপ্ত পদ্ধতি, এখনও একমাত্র defaultপদ্ধতি, এখনও একমাত্র জেডিকে পদ্ধতি এবং অন্যান্য। এই বৈশিষ্ট্যগুলির মধ্যে যে কোনওভাবেই পদ্ধতি নির্বাচনকে প্রভাবিত করা উচিত নয়।

দ্রষ্টব্য যে ঘোষণাটি পরিবর্তন করে

interface CustomIterable<T> {
    void forEach(ConsumerOne<? super T> c);
    default void forEach(ConsumerTwo<? super T> c) {}
}

এছাড়াও একটি "দ্ব্যর্থক" ত্রুটি তৈরি করে, তাই প্রয়োগযোগ্য ওভারলোডেড পদ্ধতির সংখ্যাটি কোনও বিষয় নয়, এমনকি কেবলমাত্র দুজন প্রার্থী থাকলেও defaultপদ্ধতির প্রতি সাধারণ পছন্দ নেই pre

এখনও অবধি, দুটি প্রয়োগযোগ্য পদ্ধতি এবং একটি defaultপদ্ধতি এবং উত্তরাধিকারের সম্পর্ক জড়িত থাকাকালীন সমস্যাটি উপস্থিত হতে পারে বলে মনে হয় তবে এটি আরও খননের সঠিক জায়গা নয়।


তবে এটি বোধগম্য যে আপনার উদাহরণটির রচনাগুলি সংকলকটিতে বিভিন্ন বাস্তবায়ন কোড দ্বারা পরিচালিত হতে পারে, একটি বাগ প্রদর্শন করে অন্যটি না করে।
a -> aList.add(a)একটি স্পষ্টভাবে টাইপ করা ল্যাম্বদা এক্সপ্রেশন, যা ওভারলোড রেজোলিউশনের জন্য ব্যবহার করা যায় না can't বিপরীতে, (A a) -> aList.add(a)একটি স্পষ্টত টাইপিত ল্যাম্বডা এক্সপ্রেশন যা ওভারলোডেড পদ্ধতিগুলি থেকে কোনও ম্যাচিং পদ্ধতি নির্বাচন করতে ব্যবহার করা যেতে পারে, তবে এটি এখানে সহায়তা করে না (এখানে সহায়তা করা উচিত নয়), কারণ সমস্ত পদ্ধতিতে একই কার্যকরী স্বাক্ষরের সাথে প্যারামিটারের ধরন রয়েছে ।

একটি পাল্টা উদাহরণ হিসাবে, সঙ্গে

static void forEach(Consumer<String> c) {}
static void forEach(Predicate<String> c) {}
{
  forEach(s -> s.isEmpty());
  forEach((String s) -> s.isEmpty());
}

কার্যকরী স্বাক্ষরগুলি পৃথক, এবং একটি স্পষ্টত ধরণের ল্যাম্বডা এক্সপ্রেশন ব্যবহার করে সঠিক পদ্ধতি নির্বাচন করতে সহায়তা করতে পারে যেখানে স্পষ্টভাবে টাইপ করা ল্যাম্বডা এক্সপ্রেশনটি সাহায্য করে না, তাই forEach(s -> s.isEmpty()) সংকলক ত্রুটি তৈরি করে। এবং সমস্ত জাভা সংকলকরা সে সম্পর্কে একমত।

দ্রষ্টব্য যে aList::addএকটি দ্ব্যর্থক পদ্ধতি পদ্ধতির রেফারেন্স, কারণ addপদ্ধতিটি খুব বেশি লোড হয়েছে, সুতরাং এটি কোনও পদ্ধতি নির্বাচন করতেও সহায়তা করতে পারে না, তবে পদ্ধতি রেফারেন্সগুলি যাইহোক বিভিন্ন কোড দ্বারা প্রক্রিয়াজাত হতে পারে। দ্ব্যর্থহীন হয়ে ওঠার জন্য একটি দ্ব্যর্থহীন aList::containsপরিবর্তন বা পরিবর্তিত Listকরা Collection, addআমার Eclipse ইনস্টলেশন (আমি ব্যবহৃত 2019-06) এর ফলাফলটি পরিবর্তন করিনি ।


1
@ কমেন্ট আপনার মন্তব্য করার কোন মানে নেই। ডিফল্ট পদ্ধতি হ'ল উত্তরাধিকার সূত্রে প্রাপ্ত পদ্ধতি এবং পদ্ধতিটি ওভারলোড হয়। অন্য কোন পদ্ধতি নেই। উত্তরাধিকার সূত্রে প্রাপ্ত পদ্ধতিটি হ'ল কdefault একটি অতিরিক্ত পয়েন্ট is আমার উত্তর ইতিমধ্যে একটি উদাহরণ দেখায় যেখানে গ্রহনটি ডিফল্ট পদ্ধতিতে অগ্রাধিকার দেয় না
হলগার

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

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

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

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

2

অন্ধকার কম্পাইলার সঠিকভাবে করার জন্য সমাধান করা defaultপদ্ধতি , যেহেতু এই হল সবচেয়ে নির্দিষ্ট পদ্ধতি অনুযায়ী জাভা ল্যাঙ্গুয়েজ স্পেসিফিকেশন 15.12.2.5 :

সর্বাধিক নির্দিষ্ট পদ্ধতির কোনওটি যদি কংক্রিট হয় (তবে এটি অ- abstractবা ডিফল্ট) তবে এটি সর্বাধিক নির্দিষ্ট পদ্ধতি।

javac(ডিফল্টরূপে মাভেন এবং ইন্টেলিজে দ্বারা ব্যবহৃত) পদ্ধতি কলটি এখানে অস্পষ্ট বলে is তবে জাভা ল্যাঙ্গুয়েজ স্পেসিফিকেশন অনুসারে এটি অস্পষ্ট নয় কারণ দুটি পদ্ধতির মধ্যে একটি এখানে সর্বাধিক নির্দিষ্ট পদ্ধতি।

সুস্পষ্টভাবে টাইপ করা ল্যামডা এক্সপ্রেশন পরিচালনা করা হয় স্পষ্টভাবে টাইপ করা ল্যামডা এক্সপ্রেশন চেয়ে ভিন্নভাবে জাভা। সুস্পষ্টভাবে টাইপ করা ল্যাম্বডা এক্সপ্রেশনগুলির বিপরীতে স্পষ্টভাবে টাইপ করা কঠোর অনুরোধের পদ্ধতিগুলি সনাক্ত করতে প্রথম পর্যায়ে পড়ে ( জাভা ল্যাঙ্গুয়েজ স্পেসিফিকেশন jls-15.12.2.2 , প্রথম পয়েন্ট)। সুতরাং, এখানে পদ্ধতি কলটি সুস্পষ্টভাবে টাইপ করা ল্যাম্বদা এক্সপ্রেশনগুলির জন্য অস্পষ্ট

আপনার ক্ষেত্রে, কার্যসংক্রান্ত এই জন্য javacবাগ নির্দিষ্ট করতে হয় কার্মিক ধরনের ইন্টারফেস নিম্নরূপ একটি স্পষ্টভাবে টাইপ করা ল্যামডা অভিব্যক্তি ব্যবহার পরিবর্তে:

iterable.forEach((ConsumerOne<A>) aList::add);

অথবা

iterable.forEach((Consumer<A>) aList::add);

পরীক্ষার জন্য এখানে আরও আপনার উদাহরণ ছোট করা হয়েছে:

class A {

    interface FunctionA { void f(A a); }
    interface FunctionB { void f(A a); }

    interface FooA {
        default void foo(FunctionA functionA) {}
    }

    interface FooAB extends FooA {
        void foo(FunctionB functionB);
    }

    public static void main(String[] args) {
        FooAB foo = new FooAB() {
            @Override public void foo(FunctionA functionA) {
                System.out.println("FooA::foo");
            }
            @Override public void foo(FunctionB functionB) {
                System.out.println("FooAB::foo");
            }
        };
        java.util.List<A> list = new java.util.ArrayList<A>();

        foo.foo(a -> list.add(a));      // ambiguous
        foo.foo(list::add);             // ambiguous

        foo.foo((A a) -> list.add(a));  // not ambiguous (since FooA::foo is default; javac bug)
    }

}

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

1
@ হলগার আপনার উত্তরের দাবি, "কোনও নিয়ম নেই যা উত্তরাধিকারসূত্রে প্রাপ্ত হলে বা কোনও ডিফল্ট পদ্ধতিতে নির্দিষ্ট প্রয়োগযোগ্য পদ্ধতির ক্ষেত্রে অগ্রাধিকার দেয়" " আমি কি আপনাকে সঠিকভাবে বুঝতে পারি যে আপনি বলছেন যে এই অস্তিত্বহীন নিয়মের পূর্বশর্ত এখানে প্রযোজ্য নয়? দয়া করে মনে রাখবেন, এখানে প্যারামিটারটি একটি কার্যকরী ইন্টারফেস (জেএলএস 9.8 দেখুন)।
Howlger

1
আপনি প্রসঙ্গের বাইরে একটি বাক্য ছিঁড়ে ফেলেছেন। বাক্যটি ওভাররাইড সমতুল্য পদ্ধতিগুলির নির্বাচনের বর্ণনা দেয় , অন্য কথায়, ঘোষণার মধ্যে একটি পছন্দ যা রানটাইম সময়ে সমস্ত একই পদ্ধতিতে প্রার্থনা করবে, কারণ কংক্রিটের ক্লাসে কেবল একটি কংক্রিট পদ্ধতি থাকবে। এটি যেমন স্বতন্ত্র পদ্ধতির ক্ষেত্রে অপ্রাসঙ্গিক forEach(Consumer)এবং forEach(Consumer2)যা একই বাস্তবায়ন পদ্ধতিতে শেষ হয় না।
হোলার

2
@ স্টিফান হারম্যান আমাকে জেপি বা জেএসআর চেনেন না, তবে পরিবর্তনটি "কংক্রিট" এর অর্থের সাথে সামঞ্জস্যপূর্ণ হওয়া বলে মনে হচ্ছে, অর্থাৎ জেএলএস -9.4 এর সাথে তুলনা করুন : " ডিফল্ট পদ্ধতিগুলি কংক্রিটের পদ্ধতিগুলি থেকে আলাদা (§8.4)। ৩.১), যা ক্লাসে ঘোষণা করা হয়। ”, যা কখনও পরিবর্তন হয়নি।
হোলার

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

2

Elpipse JLS §15.12.2.5 প্রয়োগ করে এমন কোডটি স্পষ্টভাবে টাইপ করা ল্যাম্বদার ক্ষেত্রে এমনকি অন্য পদ্ধতির চেয়ে কোনও নির্দিষ্ট পদ্ধতি খুঁজে পায় না।

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

আমি https://bugs.eclipse.org/562538 ফাইল করেছিএটি ট্র্যাক করতে ।

এই বিশেষ বাগটি থেকে স্বতন্ত্র, আমি কেবল এই স্টাইলের কোডের বিরুদ্ধে দৃ strongly়ভাবে পরামর্শ দিতে পারি। জাভাতে বেশ কয়েকটি চমক দেওয়ার জন্য ওভারলোডিং ভাল, ল্যাম্বদা টাইপের অনুক্রমের সাথে গুণিত, জটিলতা অনুপাতের তুলনায় বেশ অনুপাতের বাইরে।


ধন্যবাদ. ইতিমধ্যে বাগ.সেসলিপস.আর.গুগস / শো_বগ.সি.আইটি=562507 লগইন করে রেখেছিলেন , সম্ভবত আপনি তাদের লিঙ্ক করতে বা
কোনওটিকে
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.