এই 'ল্যাম্বডা' প্রত্যেকে কী কথা বলছেন?


93

এই 'ল্যাম্বডা' প্রত্যেকে কী কথা বলছেন? অনেক লোক এটি পছন্দ করে বলে মনে হচ্ছে, তবে এটি থেকে আমি যে সমস্ত সংগ্রহ করতে পারি তা হ'ল এটি কেবলমাত্র একক অভিব্যক্তিতে প্রচুর কোড লাইন ক্র্যামিংয়ের একটি উপায়।

কেউ দয়া করে আমাকে এর সত্যিকারের মূল্য সম্পর্কে আলোকিত করতে পারেন?


16
আমি কি উত্তরদাতাদের দিকে ইঙ্গিত করতে পারি যে প্রশ্নকর্তা কোথাও উল্লেখ করেন নি। নেট

4
এই সম্পর্কিত এতক্ষন পরীক্ষা stackoverflow.com/questions/16501/what-is-a-lambda-function
yesraaj

ক্র্যাকিং প্রশ্ন। জিজ্ঞাসা করার জন্য ধন্যবাদ.
চিনাবাদাম

লাম্বদাস ফাংশনাল প্রোগ্রামিং (ডিক্লারেটিভ প্রোগ্রামিং) এর জগতের অন্তর্ভুক্ত।
আরবিটি

উত্তর:


179

নাম ব্যতীত কার্যাবলী

সোজা কথায় বলতে গেলে ল্যাম্বডা হ'ল একটি নাম বা কোনও বেনামে ফাংশন। এক্সিকিউটেবল কোডের একটি ছোট টুকরো, এটি এমনভাবে পাস করা যায় যেন এটি ভেরিয়েবল। জাভাস্ক্রিপ্টে:

function () {}; // very simple

আসুন এখন এই ল্যাম্বডাসের কিছু ব্যবহার দেখুন।

বয়লারপ্লেট কোড বিমূর্ত করা হচ্ছে

ল্যাম্বডাস বয়লারপ্লেট কোড দূরে বিমূর্ত করতে ব্যবহৃত হতে পারে। উদাহরণস্বরূপ লুপস। আমরা লিখতে অভ্যস্ত forএবং whileসারা দিন লুপ। তবে এটি এমন কোড যা লিখিত হয় না। আমরা লুপের ভিতরে কোডটি বের করতে পারি, লুপটির সবচেয়ে গুরুত্বপূর্ণ অংশটি এবং বাকী অংশটি বিমূর্ত করতে পারি:

for (var i=0; i<array.length; i++) {
    // do what something useful with array[i]
}

forEachঅ্যারে অবজেক্টের ব্যবহার করে , হয়ে যায়:

array.forEach(function (element, index) {
   // do something useful with element
   // element is the equivalent of array[i] from above
});

উপরের বিমূর্ততাটি সে কার্যকর নাও হতে পারে তবে অন্যান্য উচ্চতর অর্ডার ফাংশনগুলিও রয়েছে, যেমন forEachআরও অনেক দরকারী কার্য সম্পাদন করে। উদাহরণস্বরূপ filter:

var numbers = [1, 2, 3, 4];
var even    = [];

// keep all even numbers from above array
for (var i=0; i<numbers.length; i++) {
    if (numbers[i] % 2 === 0) {
        even.push(numbers[i]);
    }
}

alert(even);

// Using the filter method
even = [1, 2, 3, 4].filter(function (number) {
    return number % 2 === 0;
});

alert(even);

কোড কার্যকর করার বিলম্ব

কিছু পরিবেশে, যেখানে ইভেন্টের ধারণা পাওয়া যায়, আমরা লাম্বডাস ব্যবহার করতে পারি এমন ঘটনাগুলির প্রতিক্রিয়া জানাতে যে সময়ে সময়ে ঘটতে পারে।

window.onload = function () {
    alert("Loaded");
};

window.setTimeout(function () {
    alert("Code executed after 2 seconds.");
}, 2000);

এটি অন্য কিছু উপায়ে করা যেতে পারে, তবে সেগুলি বরং ভার্জোজ। উদাহরণস্বরূপ, জাভাতে Runnableইন্টারফেস রয়েছে।

ফাংশন কারখানা

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

var users = [];
var getUser = function (name) {
    if (! users[name]) {
        // expensive operations to get a user. Ajax for example
        users[name] = user_from_ajax;
    }

    return users[name];
};

পরে, আমরা লক্ষ্য করতে পারি যে আমাদের একই ধরণের কার্যকারিতা রয়েছে:

var photos = [];
var getPhoto = function (name) {
    if (! photo[name]) {
        // expensive operations to get a user. Ajax for example
        photos[name] = photo_from_ajax;
    }

    return photos[name];
};

সেখানে একটি স্পষ্টভাবে একটি প্যাটার্ন রয়েছে, সুতরাং আসুন এটি বিমূর্ত করা দূরে। মেমোয়েজেশন ব্যবহার করা যাক ।

/**
 * @param {Array}     store Data structure in which we cache lambda's return values
 * @param {Function}  lambda
 * @return {Function} A function that caches the result of calling the lambda param
 */
var memoize = function (store, lambda) {
    // return a new lambda
    return function (name) {
        if (! store[name]) {
            // Execute the lambda and cache the result
            store[name] = lambda(name);
        }

        return store[name];
    };
};

var getUsers = memoize([], function (name) {
    // expensive operations to get a user. Ajax for example
});

var getPhotos = memoize([], function (name) {
    // expensive operations to get a photo. Ajax for example
});

যেমন আপনি দেখতে পাচ্ছেন, ল্যাম্বডাস ব্যবহার করে আমরা ক্যাচিং / স্মৃতি যুক্তিটি বিমূর্ত করতে সক্ষম হয়েছি। যদি অন্য উদাহরণের জন্য কিছু কর্মক্ষেত্র ছিল, আমি বিশ্বাস করি যে এই বিশেষ সমস্যাটি অন্যান্য কৌশল ব্যবহার করে খুব কমই সমাধান করা হয়েছে। আমরা কয়েকটি গুরুত্বপূর্ণ বয়লারপ্লেট কোড একক জায়গায় বের করতে পেরেছি। উল্লেখ করবেন না যে আমরা পরিত্রাণ usersএবং photosগ্লোবাল ভেরিয়েবল।

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

পাইথন ল্যাম্বডা ব্যবহারের উদাহরণ হিসাবে। উপরের কোডে যেখানে আমরা এমনকি সংখ্যার ফিল্টার করেছি পাইথনে এটির মতো উপস্থাপন করা যেতে পারে:

filter(lambda x: x % 2 == 0, [1, 2, 3, 4])

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


4
বাহ আপনি এতে অনেক সময় দিন।
এম কে 12

4
@ এমকে 12, উত্তরের আসল লেখায়, সত্যই নয়। এই জিনিসগুলি শেখার ক্ষেত্রে, হ্যাঁ, আমি শুরু করার পরে কিছুটা সময় হয়েছে :)
আয়নু জি স্টান

"ফাংশনাল ইন্টারফেস" (জাভা দৃষ্টিকোণ থেকে) সম্পর্কে ভাল উত্তর তবে অনুপস্থিত তথ্য।
জাঙ্গোফান

আপনার "অ্যাবস্ট্রাক্টিং বয়লারপ্লেট কোড" এ "===" অপারেটরগুলি কী কী?
ডন


19

"ল্যাম্বদা" শব্দটি বেনামে ফাংশন, সাধারণত একটি বন্ধ হিসাবে বোঝাতে ব্যবহৃত হয় । এগুলি কার্যকর কারণ তারা আপনাকে অযৌক্তিকভাবে আপনার কোডটি ফুলে ফেঁপে ছাড়াই অন্য ফাংশনগুলি ব্যবহার করে এমন ফাংশন লিখতে দেয়। উদাহরণস্বরূপ, রুবিতে:

(1..100).select {|num| num % 2 == 0}

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

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


6
হতে পারে আপনার যুক্ত করা উচিত যে পাইপের মধ্যে থাকা জিনিসটি প্যারামিটার। আমি এই লোকগুলির মধ্যে একজন যাদের রুবি পড়তে সমস্যা হয়।
Skurmedel

এটি একটি দুর্দান্ত উত্তর। আয়নুত আমার ভোট পাওয়ার একমাত্র কারণ হ'ল তিনি জানান যে ল্যাম্বডাস সম্পর্কে আমাদের কেন (বিস্তারিতভাবে) যত্ন নেওয়া উচিত।
ফ্র্যাঙ্ক শিয়েরার

আমি সম্মত হব না যে ল্যাম্বডাস সাধারণত ক্লোজার হয়।
jwg

6

"লাম্বদা" সম্ভবত খুব কম সংখ্যক। লাম্বদা ক্যালকুলাসটি একবার দেখুন । এটি কার্যকরী প্রোগ্রামিংয়ে দরকারী।

এবং ক্রিয়ামূলক প্রোগ্রামিং হ'ল আরও একটি প্রোগ্রামিং দৃষ্টান্ত (যেমন পদ্ধতিগত, বা বস্তু-ভিত্তিক)।


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

খুশী যে আপনি ল্যাম্বদা ক্যালকুলাসের কথা উল্লেখ করেছেন! +1
আরবিটি

5

.NET এ ল্যাম্বডাস প্রায়শই "সিনট্যাকটিক চিনি" হিসাবে উল্লেখ করা হয়। এগুলি সরাসরি কার্যকারিতা প্রভাবিত করে না তবে তারা ভাষা ব্যবহারের পক্ষে সহজ করে তোলে।

আপনি যখন এগুলি ব্যবহারের শক্তি বুঝতে পেরেছেন তখন আমি নিশ্চিত যে আপনি প্রতিনিধি / বেনামী পদ্ধতি ব্যবহার করে পুরানো স্টাইলের পদ্ধতির তুলনায় কম কোড লিখবেন।


4
আমার মনে হয় না কেউ নেট .NET উল্লেখ করেছেন, সুতরাং আরও সাধারণ উত্তর দিয়ে ওপি সম্ভবত ভাল off
মল্ফ

4
এজন্য আমি নেট সম্পর্কে জবাব দিয়ে স্পষ্ট করেছিলাম। অন্যরা যদি তাদের ভাষা প্রয়োগের বিষয়ে ঝাঁপিয়ে পড়ে থাকে তবে প্রশ্নোত্তর বহু লোককে তাদের ভাষার পছন্দ নির্বিশেষে সহায়তা করবে।
redsquare

এই উত্তরগুলি পড়ে যেমন আপনি ল্যাম্বডাস সম্পর্কে কিছু শুনেছেন তবে আপনি নিজে সেগুলি এখনও বুঝতে পারেন না।
jwg

2

ডাঃ ডবস জার্নালের ল্যাম্বডা ভাব প্রকাশের জন্য একটি দরকারী নিবন্ধ রয়েছে (সি ++ এর প্রসঙ্গে তবে আমি মনে করি আপনি যে কোনও ভাষায় নীতিগুলি প্রয়োগ করতে পারেন)।

যেমন নিবন্ধটি বলেছে: "একটি ল্যাম্বডা এক্সপ্রেশন একটি অত্যন্ত কমপ্যাক্ট এক্সপ্রেশন যা পৃথক শ্রেণি / ফাংশন সংজ্ঞা প্রয়োজন হয় না" "

সুতরাং লেখার পরিবর্তে ডিডিজে থেকে 1 এবং 2 তালিকার উদাহরণগুলি ব্যবহার করে :

std::for_each( vec.begin(), vec.end(), print_to_stream<std::string>(std::cout));

যার জন্য পৃথক শ্রেণির সংজ্ঞা প্রয়োজন যেমন:

template <typename T, typename Stream> class print_to_stream_t {
  Stream& stream_;
public:
  print_to_stream_t(Stream& s):stream_(s) {}
  void operator()(const T& t) const {
    stream_ << t;
  }
};
template <typename T,typename Stream> 
print_to_stream_t<T,Stream>   print_to_stream(Stream& s) {
  return print_to_stream_t<T,Stream>(s);
}

বুস্ট ল্যাম্বডা লাইব্রেরি ব্যবহার করে এটি হয়ে উঠতে পারে:

std::for_each(vec.begin(),vec.end(),std::cout << _1);

যা সংজ্ঞাটি ইনলাইন রাখে।

নিবন্ধটি ল্যাম্বডা এক্সপ্রেশনগুলির আরও কয়েকটি অ্যাপ্লিকেশন ব্যাখ্যা করে explains

আমি মনে করি ডিডিজে নিবন্ধের একটি মূল বিষয় হ'ল "সাধারণত, কল সাইটে ল্যাম্বডা এক্সপ্রেশনগুলি যখন ছোট এবং অতিরিক্ত জটিল ফাংশনগুলির প্রয়োজন হয় না তখন ব্যবহৃত হয় the একটি ফাংশন অবজেক্ট। "


2

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


2

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

অদ্ভুত শব্দ ব্যবহার করে গণিতবিদ এবং লজিস্টিয়ানদের দেওয়া হয়।

'ল্যাম্বদা' সত্যিই রহস্যজনক বলে মনে হচ্ছে - যেন এগুলি খুব অদ্ভুত এবং বিশেষ জিনিস। সত্যই, আপনি যদি কোনও ওয়েব ব্রাউজার অ্যাপ্লিকেশনটির জন্য জাভাস্ক্রিপ্ট লিখেন এবং "var foo = ফাংশন () {...}" আইডিয়ামটি ব্যবহার করেন, আপনি ল্যাম্বদা ফাংশনগুলি পাশাপাশি ব্যবহার করছেন।


1

একটি ল্যাম্বডা এক্সপ্রেশন ফাংশনের একটি সাধারণ রূপ is ধারণাটি হ'ল বাম দিকে ফর্মের কিছু (প্যারামিটারের সমতুল্য) ডানে (শরীরের সমতুল্য) ফর্মের কিছু হয়ে যায়।

উদাহরণস্বরূপ, সি ধারায়:

x => x * x

একটি মান বর্গাকার একটি ল্যাম্বডা। কিছু ফর্ম

x

ফর্ম কিছু হয়ে যায়

x * x

0

"একটি ল্যাম্বডা এক্সপ্রেশন একটি বেনাম ফাংশন যা অভিব্যক্তি এবং বিবৃতি ধারণ করতে পারে এবং প্রতিনিধি বা এক্সপ্রেশন গাছের ধরণের তৈরি করতে ব্যবহৃত হতে পারে।

সমস্ত ল্যাম্বদা এক্সপ্রেশন ল্যাম্বডা অপারেটর ব্যবহার করে => যা "যায়" হিসাবে পঠিত। ল্যাম্বডা অপারেটরের বাম দিকটি ইনপুট প্যারামিটারগুলি নির্দিষ্ট করে (যদি থাকে) এবং ডান দিকটি এক্সপ্রেশন বা স্টেটমেন্ট ব্লক ধারণ করে। ল্যাম্বডা এক্সপ্রেশন x => x * x পড়তে হয় "x এক্স গুনে x যায়"।

এমএসডিএন থেকে


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

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

4
এই উত্তরটি কোনও ল্যাম্বডা (অজ্ঞাতনামা ফাংশন, প্রতিনিধি, অভিব্যক্তি গাছের ধরণের সংজ্ঞা প্রয়োজন) এর মোটেই ব্যাখ্যা দেয় না এবং অবশ্যই এর মান কী তা ব্যাখ্যা করে না।
ড্যানিও

0

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

এর মূল্য? ভাল, এটি আসলে বেশ পুরানো হিসাবে বিবেচনা করে আমি বলব: যে কেউ গণনা করেন তাদের পক্ষে খুব মূল্যবান।


0

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




-1

হ্যাঁ, এটি একক অভিব্যক্তিতে কোডের প্রচুর লাইন ক্র্যামিংয়ের একটি উপায় just তবে এ জাতীয় দক্ষ ক্র্যামিং হওয়ার ফলে এটি আপনার প্রোগ্রামকে কাঠামোগত করার কয়েকটি নতুন উপায় সক্ষম করে।

প্রায়শই কেউ প্রতিনিধি বা কলব্যাক লেখার বিষয়টি এড়িয়ে যেতেন এবং পদ্ধতিগত স্টাইলে ফিরে আসতেন কারণ একক প্রকাশের জন্য নতুন ফাংশন বা ক্লাস ঘোষণা করা খুব বেশি কাজ।

ল্যাম্বডা এক্সপ্রেশনগুলি অতি ক্ষুদ্রতম কাজের জন্য কলব্যাকগুলি ব্যবহার করা সার্থক করে তোলে যা কোড আরও স্পষ্ট করে তুলতে পারে। না পারে।

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