কোন দৃশ্যে আমি একটি বিশেষ এসটিএল ধারক ব্যবহার করব?


184

আমি আমার বইয়ের সি ++ তে এসটিএল ধারকগুলি পড়ছি, বিশেষত এসটিএল এবং এর পাত্রে বিভাগটি। এখন আমি তাদের প্রত্যেকের নিজস্ব নির্দিষ্ট বৈশিষ্ট্যগুলি বুঝতে পেরেছি, এবং আমি তাদের সবগুলি মুখস্থ করার খুব কাছাকাছি ... তবে আমি এখনও বুঝতে পারি না যা তাদের প্রতিটি দৃশ্যে ব্যবহৃত হয়।

এর ব্যাখ্যা কী? উদাহরণ কোডটি অনেক বেশি পছন্দসই।


আপনি মানচিত্র, ভেক্টট, সেট ইত্যাদি বোঝাতে চান?
টমাস টেম্পেলম্যান

এমনকি এই চিত্রটি দিকে তাকিয়ে আমি বলতে পারি না কি সেরা আমার quastion ব্যবহার করার জন্য এক হতে হবে stackoverflow.com/questions/9329011/...
sergiol

2
@ এসবিআই: এটির থেকে সি ++ ফ্যাক্স ট্যাগটি সরানো এবং এটি আরও সাম্প্রতিক এবং সি ++ ১১ সহ অন্তর্ভুক্ত করা আমি কীভাবে দক্ষতার সাথে সি ++ 11 এ একটি স্ট্যান্ডার্ড লাইব্রেরি ধারক নির্বাচন করতে পারি?
অলোক

উত্তর:


336

এই ঠকাই শীটটি বিভিন্ন ধারকগুলির একটি দুর্দান্ত ভাল সংক্ষিপ্তসার সরবরাহ করে।

নীচে নীচের দিকে ফ্লোচার্টটি একটি নির্দেশিকা হিসাবে দেখুন যা বিভিন্ন ব্যবহারের পরিস্থিতিতে ব্যবহার করতে হবে:

http://linuxsoftware.co.nz/containerchoice.png

ডেভিড মুর তৈরি করেছেন এবং সিসি বাই-এসএ 3.0 এর লাইসেন্স পেয়েছেন


14
এই ফ্লোচার্টটি সোনার, আমি চাই সি # তে কিছু ছিলাম
ব্রুনো

2
আপডেট হওয়া লিঙ্ক: সি ++ পাত্রে ঠকানো শীট
বিল ডোর

3
শুরুর পয়েন্টটি অবশ্যই vectorখালি থাকতে হবে । stackoverflow.com/questions/10699265/...
eonil

5
আপনার কাছে এখন unordered_mapএবং unordered_set(এবং তাদের বহু রূপগুলি) রয়েছে যা ফ্লো চার্টে নেই তবে ভাল বাছাই করার সময় আপনি যখন অর্ডার সম্পর্কে চিন্তা করেন না তবে কী দ্বারা উপাদান খুঁজে পেতে হবে need তাদের চেহারা সাধারণত ও (লগ এন) এর পরিবর্তে ও (1) হয়।
এডিয়াকাপি

2
@ শাটল ৮87 কেবলমাত্র সেই আকারটি কখনও পরিবর্তিত হবে না, তবে আরও গুরুত্বপূর্ণ বিষয়টি আকারটি সংকলনের সময় নির্ধারিত হয় এবং কখনই তারতম্য হয় না।
ইয়ংজান

188

এখানে আমি তৈরি করেছি ডেভিড মুরের সংস্করণ (উপরে দেখুন) দ্বারা অনুপ্রাণিত একটি ফ্লোচার্ট রয়েছে যা নতুন মান (সি ++ 11) সহ আপ টু ডেট (বেশিরভাগ) is এটি কেবল আমার ব্যক্তিগত উদ্যোগ এটি, এটি অনস্বীকার্য নয়, তবে আমি অনুভব করেছি যে এটি এই আলোচনার পক্ষে মূল্যবান হতে পারে:

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


4
আপনি কি মূল উপলব্ধ করতে পারেন? এটি একটি দুর্দান্ত চার্ট। কোনও ব্লগ বা গিটহাবের উপর লেগে থাকতে পারে?
কেভিনারপে

1
এটি একটি দুর্দান্ত চার্ট। যদিও কেউ আমাকে বোঝাতে পারেন যে 'অবিচলিত অবস্থানগুলি' বলতে কী বোঝায়?
IDDQD

3
@ স্টালকার স্থায়ী অবস্থানের অর্থ হ'ল যদি আপনার ধারকটিতে কোনও উপাদানের কাছে পয়েন্টার বা পুনরুক্তিকারী থাকে তবে আপনি পাত্রে যা যুক্ত করবেন বা সরিয়েছেন তা নির্বিশেষে সেই পয়েন্টার বা পুনরুক্তিযোগ্য বৈধ থাকবে (এবং একই উপাদানটির দিকে নির্দেশ করবে) প্রশ্নযুক্ত উপাদান নয়)।
মিকেল পারসসন

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

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

42

সরল উত্তর: std::vectorআপনার অন্যথায় করার সত্যিকার কারণ না থাকলে সমস্ত কিছুর জন্য ব্যবহার করুন ।

আপনি যখন এমন কোনও কেস খুঁজে পান যেখানে আপনি ভাবছেন, "জি, std::vectorএক্স এর কারণে এখানে ভাল কাজ করে না", এক্স এর ভিত্তিতে যান


1
তবে .. পুনরুক্তি করার সময় আইটেমগুলি মুছে ফেলার / সন্নিবেশ না করা সম্পর্কে সতর্কতা অবলম্বন করুন ... এড়াতে যতদূর সম্ভব কনস্টিটেটার ব্যবহার করুন ..
vrdhn

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

13
@ ব্ল্যাক পয়েন্টটি হ'ল, ভেক্টর সাধারণত অপারেশনগুলিতে এমনকি দ্রুত হয় যা তত্ত্ব অনুসারে ধীর গতিতে কাজ করা উচিত।
বারটেক বানাচেউইচজ

1
@ বর্ধন std::remove_if"পুনরাবৃত্তির সময় মুছুন" পদ্ধতির চেয়ে প্রায় সর্বদা উচ্চতর।
ফ্রেডওভারফ্লো

1
কিছু মানদণ্ড এই আলোচনাটিকে কম বিষয়ভিত্তিক হতে সত্যিই সহায়তা করবে।
ফেলিক্স ডি

11

স্কট মায়ার্স দ্বারা কার্যকর এসটিএল দেখুন। এটি কীভাবে এসটিএল ব্যবহার করবেন তা ব্যাখ্যা করা ভাল।

আপনি যদি নির্ধারিত / নির্ধারিত সংখ্যক অবজেক্টগুলি সঞ্চয় করতে চান এবং আপনি কখনই কোনও মুছতে পারবেন না, তবে কোনও ভেক্টর যা চান তা is এটি সি অ্যারের জন্য ডিফল্ট প্রতিস্থাপন এবং এটি একটির মতো কাজ করে তবে ওভারফ্লো হয় না। রিজার্ভ () এর সাথে আপনি এর আকার আগেই সেট করতে পারেন।

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

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

আপনার যদি কী-মূল্যের অনেকগুলি জুটি থাকে এবং আপনি কী দ্বারা তা বাছাই করতে চান তবে একটি মানচিত্র কার্যকর ... তবে এটি প্রতি কী অনুসারে একটি মান রাখবে। আপনার যদি প্রতি কী প্রতি একাধিক মানের প্রয়োজন হয় তবে মানচিত্রে আপনার মান হিসাবে ভেক্টর / তালিকা থাকতে পারে বা মাল্টিম্যাপ ব্যবহার করতে পারেন।

এটি এসটিএলে নেই, তবে এটি এসটিএল-এর টিআর 1 আপডেটে রয়েছে: আপনি যদি কী-মূল্যের অনেকগুলি জুটি ব্যবহার করেন যা আপনি কী দ্বারা সন্ধান করতে যাচ্ছেন এবং যদি আপনি তাদের ক্রমটির যত্ন নেন না, আপনি সম্ভবত একটি হ্যাশ ব্যবহার করতে চান - যা tr1 :: আনর্ডারড_ম্যাপ। আমি এটি ভিজ্যুয়াল সি ++ 7.1 দিয়ে ব্যবহার করেছি, যেখানে এটিকে স্টেডেক্সট :: হ্যাশ_ম্যাপ বলা হত। মানচিত্রে ও (লগ এন) এর অনুসন্ধানের পরিবর্তে এটি ও (1) এর অনুসন্ধান রয়েছে।


আমি এখন বেশ কয়েকটা উপাখ্যান শুনেছি যে মাইক্রোসফ্ট hash_mapখুব ভাল বাস্তবায়ন নয় ing আমি আশা করি তারা আরও ভাল করেছে unordered_map
মার্ক রান্সম

3
তালিকার মধ্যে - "আপনি ক্রমিকভাবে কোনও উপাদান অ্যাক্সেস করতে পারবেন না।" - আমার মনে হয় আপনি বোঝাতে চাইছেন আপনি সরাসরি কোনও উপাদানকে এলোমেলোভাবে অ্যাক্সেস বা সূচক করতে পারবেন না ....
টনি ডেলরয়

^ হ্যাঁ, কারণ ক্রমবর্ধমান অ্যাক্সেস হ'ল একটি listকাজটি করে। বরং সেখানে ত্রুটিপূর্ণ ত্রুটি।
আন্ডারস্কোর_

7

আমি 3 টি বৈশিষ্ট্যযুক্ত ফ্লোচার্টটিকে নতুন করে ডিজাইন করেছি:

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

ফ্লোচার্ট: এখানে চিত্র বর্ণনা লিখুন

এই লিঙ্কে আরও তথ্য সরবরাহ করা হয়েছে ।


5

একটি গুরুত্বপূর্ণ পয়েন্ট সংক্ষেপে এতদূর উল্লেখ করা হয়েছে, যে আপনি যদি সংলগ্ন মেমরির প্রয়োজন (যেমন একটি সি অ্যারে দেয়), তারপর আপনি শুধুমাত্র ব্যবহার করতে পারেন vector, arrayঅথবা string

arrayআকারটি সংকলনের সময় জানা থাকলে ব্যবহার করুন ।

আপনার stringযদি কেবলমাত্র চরিত্রের ধরণের সাথে কাজ করার প্রয়োজন হয় এবং একটি স্ট্রিং প্রয়োজন হয় তবে কেবল সাধারণ-উদ্দেশ্যে ধারক নয় Use

vectorঅন্যান্য সমস্ত ক্ষেত্রে ব্যবহার করুন ( vectorযাইহোক বেশিরভাগ ক্ষেত্রে ধারকটির ডিফল্ট পছন্দ হওয়া উচিত)।

এই তিনটি data()দিয়েই আপনি ধারকটির প্রথম উপাদানটির একটি পয়েন্টার পেতে সদস্য ফাংশনটি ব্যবহার করতে পারেন ।


3

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

vector: অন্তর্ভুক্ত অবজেক্ট প্রতি সামান্য বা কোনও মেমরির ওভারহেড সহ কমপ্যাক্ট লেআউট। পুনরাবৃত্তি করার জন্য দক্ষ। সংযোজন, সন্নিবেশ করা এবং মোছা ব্যয়বহুল হতে পারে বিশেষত জটিল বস্তুর জন্য। সূচী দ্বারা কোনও অন্তর্ভুক্ত বস্তু সন্ধান করা সস্তা, যেমন মাইভেক্টর [10]। যেখানে আপনি সি তে একটি অ্যারে ব্যবহার করেছেন সেখানে ব্যবহার করুন ভাল যেখানে আপনার প্রচুর সরল অবজেক্ট রয়েছে (উদাঃ int)। reserve()ধারকটিতে প্রচুর অবজেক্ট যুক্ত করার আগে ব্যবহার করতে ভুলবেন না ।

list: অন্তর্ভুক্ত অবজেক্ট প্রতি ছোট মেমরি ওভারহেড। পুনরাবৃত্তি করার জন্য দক্ষ। সংযোজন, সন্নিবেশ এবং মোছা সস্তা। যেখানে আপনি সি তে একটি লিঙ্কযুক্ত তালিকা ব্যবহার করেছেন Use

set(এবং multiset): অন্তর্ভুক্ত অবজেক্টের জন্য উল্লেখযোগ্য মেমরি ওভারহেড। যদি সেই ধারকটিতে একটি প্রদত্ত বস্তু থাকে বা দক্ষতার সাথে পাত্রে মার্জ করা হয় তবে আপনাকে দ্রুত কোথায় তা সন্ধান করতে হবে তা ব্যবহার করুন।

map(এবং multimap): অন্তর্ভুক্ত অবজেক্টের জন্য উল্লেখযোগ্য মেমরি ওভারহেড। আপনি কী-মান যুক্তগুলি সংরক্ষণ করতে চান তা ব্যবহার করুন এবং কী দ্বারা মানগুলি দ্রুত সন্ধান করুন।

জেডদান দ্বারা প্রস্তাবিত ঠকানো শীটের প্রবাহের চার্টটি আরও কার্যকর গাইড সরবরাহ করে।


"অন্তর্ভুক্ত অবজেক্ট প্রতি ছোট মেমরি ওভারহেড" তালিকার পক্ষে সত্য নয়। std :: তালিকাকে দ্বিগুণ সংযুক্ত তালিকার হিসাবে প্রয়োগ করা হয় এবং তাই এটি প্রতি সঞ্চিত বস্তুতে 2 পয়েন্টার বজায় রাখে যা অবহেলা নয়।
হান্না খলিল

আমি এখনও "ছোট" হিসাবে সঞ্চিত বস্তুতে দুটি পয়েন্টার গণনা করব।
বিড

কি তুলনা? স্টাড :: ফরওয়ার্ডলিস্ট হল এমন একটি ধারক যা মূলত প্রতি বস্তুতে কম মেটা-ডেটা সঞ্চয় করার পরামর্শ দেওয়া হয়েছিল (কেবলমাত্র একটি পয়েন্টার)। যখন std :: ভেক্টর প্রতি বস্তুতে 0 মেটা ডেটা ধারণ করে। সুতরাং 2 টি পয়েন্টার অন্যান্য
হানা খলিল

এটি সব আপনার বস্তুর আকারের উপর নির্ভর করে। আমি ইতিমধ্যে বলেছি যে ভেক্টরটির "কমপ্যাক্ট লেআউট থাকে কম থাকা বা কোনও স্মৃতিতে ওভারহেডের সাথে থাকা বস্তুটি নেই"। আমি এখনও বলব যে সেটটি সেট এবং মানচিত্রের তুলনায় তালিকার একটি ছোট মেমরি ওভারহেড রয়েছে এবং ভেক্টরের চেয়ে কিছুটা বড় মেমরির ওভারহেড রয়েছে। আপনি কোন বিষয়টিকে টিবিএইচ করার চেষ্টা করছেন তা আমি সত্যই নিশ্চিত নই!
বিড

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

2

একটি পাঠ আমি শিখেছি হ'ল: এটি কোনও শ্রেণিতে গুটিয়ে রাখার চেষ্টা করুন, যেহেতু প্রথম দিনটিতে ধারক টাইপ পরিবর্তন করা বড় চমক পেতে পারে।

class CollectionOfFoo {
    Collection<Foo*> foos;
    .. delegate methods specifically 
}

সামনের দিকে এটি খুব বেশি ব্যয় করে না, এবং যখনই কেউ এই কাঠামোটিতে অপারেশন এক্স করেন তখন আপনি যখন ব্রেক করতে চান তখন ডিবাগিংয়ে সময় সাশ্রয় করে।

একটি কাজের জন্য নিখুঁত ডেটা কাঠামো নির্বাচন করতে আসছে:

প্রতিটি ডেটা স্ট্রাকচার কিছু ক্রিয়াকলাপ সরবরাহ করে যা বিভিন্ন সময়ের জটিলতা হতে পারে:

ও (1), ও (এলজি এন), ও (এন), ইত্যাদি

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

সরল, তাই না (-:


5
এই কারণেই আমরা পুনরায় ব্যবহারকারীর ব্যবহার করি না?
প্ল্যাটিনাম আজুর

@ প্ল্যাটিনামএজুর এমনকি আইট্রেটার সদস্য হওয়া উচিত টাইপডেফ .. আপনি যদি ধারক প্রকারটি পরিবর্তন করেন তবে আপনাকেও যেতে হবে এবং সমস্ত পুনরাবৃত্ত সংজ্ঞা পরিবর্তন করতে হবে ... যদিও এটি সি ++ 1x স্থির হয়েছে!
vrdhn

4
অদ্ভুত এক জন্য, এই সি ++ 11 ফিক্স হল: auto myIterator = whateverCollection.begin(); // <-- immune to changes of container type
ব্ল্যাক

1
একটি হবে typedef Collection<Foo*> CollectionOfFoo;যথেষ্ট হবে?
ক্রেগ ম্যাককুইন

5
এটি পরে খুব সম্ভবত আপনার মন পরিবর্তন করতে পারে এবং কেবল কোনও ভিন্ন
ধারককে

1

আমি মিকেল পারসনের দুর্দান্ত ফ্লোচার্টটি প্রসারিত করেছি । আমি কয়েকটি ধারক বিভাগ, অ্যারে ধারক এবং কিছু নোট যুক্ত করেছি। আপনি যদি নিজের কপিটি চান, এখানে গুগল অঙ্কন। ধন্যবাদ, মিকেল ভিত্তিক কাজ করার জন্য! সি ++ পাত্রে পিকার


1

আমি এটি অন্য একটি প্রশ্নের উত্তর দিয়েছি যা এটির দ্বৈত হিসাবে চিহ্নিত হয়েছে। তবে আমি অনুভব করি যে মানক ধারক চয়ন করার সিদ্ধান্ত সম্পর্কিত কিছু ভাল নিবন্ধ উল্লেখ করা ভাল nice

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

Https://isocpp.org/blog/2014/06/stroustrup-lists নিবন্ধটির লিঙ্কটি এখানে

এবং একটি থেকে উদ্ধৃতি,

এবং হ্যাঁ, আমার প্রস্তাবটি ডিফল্টরূপে std :: ভেক্টর ব্যবহার করা to

মন্তব্যে, ব্যবহারকারী @ নাথানঅলিভার আরও একটি ভাল ব্লগ সরবরাহ করেছেন, যার পরিমাপ আরও বেশি। https://baptiste-wicht.com/posts/2012/12/cpp-benchmark-vector-list-deque.html

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