জিএনইউ প্রোলগ, 98 বাইট
b(x,0,x).
b(T/H,N,H):-N#=A+B+1,b(H,A,_),b(T,B,J),H@>=J.
c(X,Y):-findall(A,b(A,X,_),L),length(L,Y).
এই উত্তরটি কীভাবে এমনকি সহজ আই / ও ফর্ম্যাটগুলির সাথে লড়াই করতে পারে তার একটি দুর্দান্ত উদাহরণ। এটি সমস্যার সমাধানের জন্য অ্যালগরিদমের পরিবর্তে সমস্যা বর্ণনা করার মাধ্যমে সত্য প্রোলোগ স্টাইলে কাজ করে: এটি আইনগত বুদ্বুদ বিন্যাস হিসাবে গণ্য তা নির্দিষ্ট করে, প্রোলোগকে সমস্ত বুদ্বুদ বিন্যাস উত্পন্ন করতে বলে এবং তারপরে সেগুলি গণনা করে। প্রজন্ম 55 টি অক্ষর নেয় (প্রোগ্রামের প্রথম দুটি লাইন)। গণনা এবং I / O অন্যান্য 43 টি (তৃতীয় লাইন এবং নতুন লাইন যা দুটি অংশকে পৃথক করে) নেয়। আমি বাজি দিয়েছি যে এটি কোনও সমস্যা নয় যেটি ওপি ভাষাগুলি I / O এর সাথে লড়াইয়ের কারণ হিসাবে প্রত্যাশা করেছিল! (দ্রষ্টব্য: স্ট্যাক এক্সচেঞ্জের সিনট্যাক্স হাইলাইটিং এটিকে পড়া সহজ করে তোলে, সহজ নয়, তাই আমি এটি বন্ধ করে দিয়েছি)।
ব্যাখ্যা
আসুন একটি একই প্রোগ্রামের সিউডোকোড সংস্করণ দিয়ে শুরু করি যা আসলে কাজ করে না:
b(Bubbles,Count) if map(b,Bubbles,BubbleCounts)
and sum(BubbleCounts,InteriorCount)
and Count is InteriorCount + 1
and is_sorted(Bubbles).
c(Count,NPossibilities) if listof(Bubbles,b(Bubbles,Count),List)
and length(List,NPossibilities).
এটি কীভাবে b
কাজ করে তা মোটামুটি পরিষ্কার হওয়া উচিত : আমরা বাছাই করা তালিকার মাধ্যমে বুদবুদগুলি উপস্থাপন করছি (যেগুলি মাল্টিসেটগুলির একটি সাধারণ বাস্তবায়ন যা সমান মাল্টিসেটগুলির সাথে সমান তুলনা করে) এবং একটি একক বুদবুদের []
একটি গণনা হয় 1 এর সাথে একটি বৃহত বুদ্বুদ রয়েছে প্লাস 1 এর ভিতরে বুদবুদগুলির মোট গণনার সমান 4 টি গণনার জন্য এই প্রোগ্রামটি (যদি এটি কাজ করে) নীচের তালিকা তৈরি করে:
[[],[],[],[]]
[[],[],[[]]]
[[],[[],[]]]
[[],[[[]]]]
[[[]],[[]]]
[[[],[],[]]]
[[[],[[]]]]
[[[[],[]]]]
[[[[[]]]]]
এই প্রোগ্রামটি বেশ কয়েকটি কারণে উত্তর হিসাবে অনুপযুক্ত, তবে সবচেয়ে চাপের বিষয়টি হ'ল প্রোলোগের আসলে কোনও প্রিকারেট নেই map
(এবং এটি লেখার ক্ষেত্রে অনেক বেশি বাইট লাগবে)। সুতরাং পরিবর্তে, আমরা প্রোগ্রামটি আরও এভাবে লিখি:
b([], 0).
b([Head|Tail],Count) if b(Head,HeadCount)
and b(Tail,TailCount)
and Count is HeadCount + TailCount + 1
and is_sorted([Head|Tail]).
c(Count,NPossibilities) if listof(Bubbles,b(Bubbles,Count),List)
and length(List,NPossibilities).
এখানে অন্য প্রধান সমস্যাটি হ'ল প্রোলোগের মূল্যায়ন আদেশ যেভাবে কাজ করে তা চালানোর সময় এটি চালানোর সময় একটি অসীম লুপের মধ্যে চলে যাবে। তবে আমরা প্রোগ্রামটি সামান্য পুনরায় সাজিয়ে অসীম লুপটি সমাধান করতে পারি:
b([], 0).
b([Head|Tail],Count) if Count #= HeadCount + TailCount + 1
and b(Head,HeadCount)
and b(Tail,TailCount)
and is_sorted([Head|Tail]).
c(Count,NPossibilities) if listof(Bubbles,b(Bubbles,Count),List)
and length(List,NPossibilities).
এই মোটামুটি অদ্ভুত চেহারা পারে - আমরা একসাথে গন্য যোগ করছি আগে আমরা কি তারা জানেন - কিন্তু গনুহ Prolog এর #=
noncausal গাণিতিক যে সাজানোর হ্যান্ডলিং করতে সক্ষম, এবং কারণ এটি প্রথম লাইন b
, এবং HeadCount
এবং TailCount
অবশ্যই উভয় চেয়ে কম হতে Count
(যা পরিচিত), এটি প্রাকৃতিকভাবে পুনরাবৃত্তির পদটি কতবার মেলতে পারে তা সীমাবদ্ধ করার একটি পদ্ধতি হিসাবে কাজ করে এবং ফলস্বরূপ প্রোগ্রামটি সর্বদা শেষ করে দেয়।
পরবর্তী পদক্ষেপটি কিছুটা নিচে গল্ফ করা। হোয়াইটস্পেস সরিয়ে, একক-অক্ষর ভেরিয়েবল নাম ব্যবহার করে, এর :-
জন্য if
এবং এর ,
জন্য সংক্ষিপ্তসারগুলি and
ব্যবহার setof
না করে listof
(এর সংক্ষিপ্ত নাম রয়েছে এবং এক্ষেত্রে একই ফলাফল প্রকাশ করে) এবং এর sort0(X,X)
পরিবর্তে ব্যবহার করে is_sorted(X)
(কারণ is_sorted
বাস্তবে আসল ফাংশন নয়, আমি এটি তৈরি করেছি):
b([],0).
b([H|T],N):-N#=A+B+1,b(H,A),b(T,B),sort0([H|T],[H|T]).
c(X,Y):-setof(A,b(A,X),L),length(L,Y).
এটি মোটামুটি সংক্ষিপ্ত, তবে আরও ভাল করা সম্ভব। মূল অন্তর্দৃষ্টিটি হ'ল [H|T]
তালিকার সিনট্যাক্সগুলি যেতেই সত্যই ভার্জোজ ose যেমন লিস্প প্রোগ্রামাররা জানতে পারবেন, একটি তালিকা মূলত স্রেফ কনস সেলগুলি দিয়ে তৈরি, যা মূলত কেবলমাত্র টিপলস, এবং এই প্রোগ্রামের কোনও অংশই তালিকা বিল্টিন ব্যবহার করছে না। প্রোলোগের বেশ কয়েকটি সংক্ষিপ্ত টুপল সিনট্যাক্স রয়েছে (আমার প্রিয়টি A-B
তবে আমার দ্বিতীয় প্রিয়টি A/B
, যা আমি এখানে ব্যবহার করছি কারণ এটি এক্ষেত্রে সহজেই পঠন-পাঠযোগ্য ডিবাগ আউটপুট তৈরি করে); এবং আমরা nil
দ্বি-চরিত্রের সাথে আটকে থাকার পরিবর্তে তালিকার শেষের জন্য আমাদের নিজস্ব একক-চরিত্রটিও []
বেছে নিতে পারি (আমি বেছে নিয়েছি x
, তবে মূলত কিছুতেই কাজ করে না)। এর পরিবর্তে [H|T]
, আমরা ব্যবহার করতে পারি T/H
এবং এর থেকে আউটপুট পেতে পারিb
এটি দেখতে এইরকম দেখাচ্ছে (নোট করুন যে টিপলসগুলিতে বাছাই ক্রমের তালিকাগুলির তুলনায় কিছুটা আলাদা, সুতরাং এগুলি উপরের মতো একই ক্রমে নয়):
x/x/x/x/x
x/x/x/(x/x)
x/(x/x)/(x/x)
x/x/(x/x/x)
x/(x/x/x/x)
x/x/(x/(x/x))
x/(x/x/(x/x))
x/(x/(x/x/x))
x/(x/(x/(x/x)))
উপরের নেস্টেড তালিকার চেয়ে এটি পড়া আরও শক্ত, তবে এটি সম্ভব; মানসিকভাবে এসগুলি এড়িয়ে যান এবং একটি বুদ্বুদ হিসাবে x
ব্যাখ্যা করুন /()
(বা /
কোনও বিষয়বস্তু সহ একটি অবনমিত বুদ্বুদ হিসাবে কেবল প্লেইন , যদি এর ()
পরে কিছুই না থাকে), এবং উপাদানগুলির উপরে তালিকাভুক্ত সংস্করণটির সাথে 1-থেকে -1 (বিশৃঙ্খলা থাকলে) চিঠিপত্র রয়েছে ।
অবশ্যই, এই তালিকার উপস্থাপনা, অনেক খাটো হওয়া সত্ত্বেও, একটি বড় অপূর্ণতা রয়েছে; এটি ভাষার অন্তর্নির্মিত নয়, সুতরাং sort0
আমাদের তালিকাটি বাছাই করা হয়েছে কিনা তা পরীক্ষা করে আমরা ব্যবহার করতে পারি না । sort0
যাইহোক, মোটামুটি ভার্জোজ, যদিও এটি হাতে হাতে করা কোনও বিশাল ক্ষতি নয় (বাস্তবে, [H|T]
তালিকার উপস্থাপনের মাধ্যমে হাত দ্বারা এটি করা ঠিক একই সংখ্যক বাইটে আসে)। এখানে মূল অন্তর্দৃষ্টিটি হ'ল প্রোগ্রামটি লিখিত চেক হিসাবে তালিকাটি বাছাই করা হয়েছে কিনা, এর লেজটি বাছাই করা হয়েছে কিনা, যদি তার লেজটি সাজানো হয়, ইত্যাদি see প্রচুর রিলান্ড্যান্ট চেক রয়েছে এবং আমরা এটি কাজে লাগাতে পারি। পরিবর্তে, আমরা কেবল প্রথম দুটি উপাদান ক্রমযুক্ত কিনা তা নিশ্চিত করে পরীক্ষা করব (যা নিশ্চিত করে যে তালিকাটি তালিকাটি নিজেই তালিকা অনুসারে বাছাই করা হবে এবং এর সমস্ত প্রত্যয় চেক হয়ে গেছে)।
প্রথম উপাদানটি সহজেই অ্যাক্সেসযোগ্য; এটাই কেবল তালিকার শীর্ষস্থানীয় H
। দ্বিতীয় উপাদানটি অ্যাক্সেস করা বরং আরও শক্ত, যদিও এটি বিদ্যমান নেই। ভাগ্যক্রমে, x
আমরা যে সমস্ত টিপলগুলি বিবেচনা করছি তার চেয়ে কম (প্রোলোগের জেনারেলাইজড তুলনা অপারেটরের মাধ্যমে @>=
), তাই আমরা একক তালিকার "দ্বিতীয় উপাদান "টিকে বিবেচনা করতে পারি x
এবং প্রোগ্রামটি ঠিকঠাক কাজ করবে work প্রকৃতপক্ষে দ্বিতীয় উপাদানটি অ্যাক্সেস করার ক্ষেত্রে, সবচেয়ে ক্ষুদ্রতর পদ্ধতিটি একটি তৃতীয় যুক্তি (একটি আউট আর্গুমেন্ট) যুক্ত করা হয় b
, যা x
বেস কেসে এবং H
পুনরাবৃত্তির ক্ষেত্রে ফিরে আসে; এর অর্থ হ'ল আমরা দ্বিতীয় পুনরাবৃত্ত কল থেকে আউটপুট হিসাবে লেজের মাথাটি ধরতে পারি B
, এবং অবশ্যই লেজের মাথাটি তালিকার দ্বিতীয় উপাদান। এখন b
এটির মতো দেখাচ্ছে:
b(x,0,x).
b(T/H,N,H):-N#=A+B+1,b(H,A,_),b(T,B,J),H@>=J.
বেস কেসটি যথেষ্ট সহজ (খালি তালিকা, 0 এর একটি গণনা ফেরান, খালি তালিকার "প্রথম উপাদান" হ'ল x
)। Recursive ক্ষেত্রে আগের মতই পথ শুরু হয় (ঠিক সঙ্গে T/H
স্বরলিপি বদলে [H|T]
, এবং H
যুক্তি আউট একটি অতিরিক্ত হিসাবে); আমরা মাথার रिकर्सিভ কল থেকে অতিরিক্ত যুক্তি উপেক্ষা করি, তবে এটি J
পুচ্ছের পুনরাবৃত্ত কলটিতে সঞ্চয় করি । তারপর আমরা যা করতে হবে তা নিশ্চিত করা হয় H
তার চেয়ে অনেক বেশী বা সমান J
অনুক্রমে (যেমন "যদি তালিকা অন্তত দুটি উপাদান আছে, প্রথম চেয়ে মহান বা দ্বিতীয় সমান) তা নিশ্চিত করার জন্য তালিকা প্রান্ত পর্যন্ত সাজানো।
দুর্ভাগ্যক্রমে, setof
যদি আমরা c
এই নতুন সংজ্ঞাটির সাথে পূর্ববর্তী সংজ্ঞাটি একসাথে ব্যবহার করার চেষ্টা করি তবে b
এটি কোনও এসকিউএল হিসাবে অব্যবহৃত পরামিতিগুলিকে আরও কম-বেশি একইভাবে আচরণ করে GROUP BY
, যা আমরা যা চাই তা সম্পূর্ণ নয়। আমরা যা চাই তা করার জন্য এটি পুনরায় কনফিগার করা সম্ভব, তবে সেই পুনর্গঠনটির জন্য অক্ষরগুলির ব্যয় হয়। পরিবর্তে, আমরা ব্যবহার করি findall
, যার একটি আরও সুবিধাজনক ডিফল্ট আচরণ রয়েছে এবং এটি কেবলমাত্র দুটি অক্ষর দীর্ঘ, আমাদের এই সংজ্ঞাটি প্রদান করে c
:
c(X,Y):-findall(A,b(A,X,_),L),length(L,Y).
এবং এটি সম্পূর্ণ প্রোগ্রাম; অবিচ্ছিন্নভাবে বুদ্বুদ নিদর্শন উত্পন্ন করে, তারপরে গণনা করে পুরো লোড ব্যয় করুন ( findall
জেনারেটরকে একটি তালিকায় রূপান্তর করার জন্য আমাদের একটি দীর্ঘ দীর্ঘ সময় প্রয়োজন , তারপরে দুর্ভাগ্যক্রমে length
সেই তালিকার দৈর্ঘ্য যাচাই করার জন্য অক্ষরে অক্ষরে নামকরণ , একটি ফাংশন ঘোষণার জন্য বয়লারপ্লেট)।