আমি কীভাবে পূর্ব / পোস্টের শর্তাদি এবং আক্রমণকারীদের সঠিক লুপটি বিকাশের জন্য ব্যবহার করতে পারি তার আরও বিশদ উদাহরণ দিতে চলেছি। একসাথে যেমন জোর দেওয়া হয় একটি স্পেসিফিকেশন বা চুক্তি বলা হয়।
আমি আপনাকে প্রতি লুপের জন্য এটি করার চেষ্টা করার পরামর্শ দিচ্ছি না। তবে আমি আশা করি যে আপনি চিন্তার প্রক্রিয়া জড়িত তা দেখতে দরকারী হবেন।
এটি করার জন্য, আমি আপনার পদ্ধতিটি মাইক্রোসফ্ট ড্যাফনি নামে একটি সরঞ্জামে অনুবাদ করব , যা এই জাতীয় স্পেসিফিকেশনের যথার্থতা প্রমাণ করার জন্য তৈরি করা হয়েছে। এটি প্রতিটি লুপের সমাপ্তিও পরীক্ষা করে। দয়া করে নোট করুন যে ড্যাফনির কোনও for
লুপ নেই তাই তার while
পরিবর্তে আমাকে একটি লুপ ব্যবহার করতে হয়েছিল।
অবশেষে আমি দেখাব যে আপনি কীভাবে আপনার লুপের সামান্য সহজ সংস্করণটি ডিজাইনে এই জাতীয় স্পেসিফিকেশন ব্যবহার করতে পারেন। এই সহজ লুপ সংস্করণটি আসলে লুপের শর্ত j > 0
এবং অ্যাসাইনমেন্টটি দেয় না array[j] = value
- যেমনটি আপনার প্রাথমিক অন্তর্দৃষ্টি ছিল।
ড্যাফনি আমাদের পক্ষে প্রমাণ করবে যে এই দুটি লুপই সঠিক এবং একই কাজ।
তারপরে আমি আমার অভিজ্ঞতার উপর ভিত্তি করে সঠিক পিছনের লুপটি কীভাবে লিখব সে সম্পর্কে একটি সাধারণ দাবি করব, যা ভবিষ্যতে এই পরিস্থিতির মুখোমুখি হলে সম্ভবত আপনাকে সহায়তা করবে।
প্রথম খণ্ড - পদ্ধতির জন্য একটি স্পেসিফিকেশন রচনা
আমরা যে প্রথম চ্যালেঞ্জের মুখোমুখি হচ্ছি তা হ'ল পদ্ধতিটি আসলে কী করা উচিত তা নির্ধারণ করে। এই লক্ষ্যে আমি প্রাক এবং পোস্ট শর্তাদি ডিজাইন করেছি যা পদ্ধতির আচরণ নির্দিষ্ট করে। স্পেসিফিকেশনটি আরও নির্ভুল করতে আমি পদ্ধতিটি বাড়িয়েছি যেখানে value
এটি সন্নিবেশ করা সূচকটি ফিরে আসল ।
method insert(arr:array<int>, rightIndex:int, value:int) returns (index:int)
// the method will modify the array
modifies arr
// the array will not be null
requires arr != null
// the right index is within the bounds of the array
// but not the last item
requires 0 <= rightIndex < arr.Length - 1
// value will be inserted into the array at index
ensures arr[index] == value
// index is within the bounds of the array
ensures 0 <= index <= rightIndex + 1
// the array to the left of index is not modified
ensures arr[..index] == old(arr[..index])
// the array to the right of index, up to right index is
// shifted to the right by one place
ensures arr[index+1..rightIndex+2] == old(arr[index..rightIndex+1])
// the array to the right of rightIndex+1 is not modified
ensures arr[rightIndex+2..] == old(arr[rightIndex+2..])
এই স্পেসিফিকেশন পদ্ধতির আচরণকে পুরোপুরি ক্যাপচার করে। এই স্পেসিফিকেশন সম্পর্কে আমার প্রধান পর্যবেক্ষণ হ'ল প্রক্রিয়াটি মানটির rightIndex+1
পরিবর্তে মান পাস করা হলে এটি সরল করা হবে rightIndex
। তবে যেহেতু আমি এই পদ্ধতিটি কোথা থেকে কল করা হয়েছে তা দেখতে পাচ্ছি না আমি জানি না যে এই পরিবর্তনটি প্রোগ্রামের বাকি অংশগুলিতে কী প্রভাব ফেলবে।
পার্ট টু - একটি লুপ আক্রমণকারী নির্ধারণ করা
পদ্ধতির আচরণের জন্য এখন আমাদের একটি স্পেসিফিকেশন রয়েছে, আমাদের লুপ আচরণের একটি স্পেসিফিকেশন যুক্ত করতে হবে যা ড্যাফনিকে বুঝিয়ে দেবে যে লুপটি কার্যকর করে লুপটি শেষ হয়ে যাবে এবং এর ফলস্বরূপ কাঙ্ক্ষিত চূড়ান্ত অবস্থার ফলাফল ঘটবে array
।
নীচে আপনার মূল লুপটি যুক্ত করা হয়েছে, লুপের আক্রমণকারীদের সাথে ড্যাফনি সিনট্যাক্সে অনুবাদ করা হয়েছে। আমি মানটি সন্নিবেশ করা সূচকটি ফেরাতেও এটি পরিবর্তন করেছি।
{
// take a copy of the initial array, so we can refer to it later
// ghost variables do not affect program execution, they are just
// for specification
ghost var initialArr := arr[..];
var j := rightIndex;
while(j >= 0 && arr[j] > value)
// the loop always decreases j, so it will terminate
decreases j
// j remains within the loop index off-by-one
invariant -1 <= j < arr.Length
// the right side of the array is not modified
invariant arr[rightIndex+2..] == initialArr[rightIndex+2..]
// the part of the array looked at by the loop so far is
// shifted by one place to the right
invariant arr[j+2..rightIndex+2] == initialArr[j+1..rightIndex+1]
// the part of the array not looked at yet is not modified
invariant arr[..j+1] == initialArr[..j+1]
{
arr[j + 1] := arr[j];
j := j-1;
}
arr[j + 1] := value;
return j+1; // return the position of the insert
}
এটি ড্যাফনিতে যাচাই করা হয়। আপনি নিজে এই লিঙ্কটি অনুসরণ করে দেখতে পারেন । সুতরাং আপনার লুপটি সঠিকভাবে প্রয়োগ করে মেথড স্পেসিফিকেশন যা আমি প্রথম অংশে লিখেছি। এই পদ্ধতির স্পেসিফিকেশনটি আপনি যে আচরণটি চেয়েছিলেন তা আসলেই আপনাকে সিদ্ধান্ত নিতে হবে।
নোট করুন যে ড্যাফনি এখানে নির্ভুলতার একটি প্রমাণ তৈরি করছে। এটি পরীক্ষার মাধ্যমে সম্ভবত প্রাপ্তির চেয়ে নির্ভুলতার আরও শক্তিশালী গ্যারান্টি।
পার্ট থ্রি - একটি সরল লুপ
এখন আমাদের একটি পদ্ধতি স্পেসিফিকেশন রয়েছে যা লুপের আচরণকে ক্যাপচার করে। আমরা এখনও লুপের আচরণ পরিবর্তন করি নি বলে আত্মবিশ্বাস বজায় রেখে আমরা নিরাপদে লুপটির বাস্তবায়নটি পরিবর্তন করতে পারি।
আমি লুপটি সংশোধন করেছি যাতে এটি লুপের শর্ত এবং আপনার চূড়ান্ত মান সম্পর্কে আপনার মূল স্বজ্ঞাতগুলির সাথে মেলে j
। আমি যুক্তি দিয়ে বলব যে আপনি নিজের প্রশ্নে বর্ণিত লুপের চেয়ে এই লুপটি সহজ। এটি প্রায়শই j
বরং ব্যবহার করতে সক্ষম হয় j+1
।
জে শুরু করুন rightIndex+1
লুপের শর্তটি এতে পরিবর্তন করুন j > 0 && arr[j-1] > value
অ্যাসাইনমেন্টটি এতে পরিবর্তন করুন arr[j] := value
ভিক্ষার চেয়ে লুপের শেষে লুপের কাউন্টারটি হ্রাস করুন
কোডটি এখানে। নোট করুন যে লুপ আক্রমণকারীরা এখন আরও কিছুটা সহজ লিখেছেন:
method insert2(arr:array<int>, rightIndex:int, value:int) returns (index:int)
modifies arr
requires arr != null
requires 0 <= rightIndex < arr.Length - 1
ensures 0 <= index <= rightIndex + 1
ensures arr[..index] == old(arr[..index])
ensures arr[index] == value
ensures arr[index+1..rightIndex+2] == old(arr[index..rightIndex+1])
ensures arr[rightIndex+2..] == old(arr[rightIndex+2..])
{
ghost var initialArr := arr[..];
var j := rightIndex+1;
while(j > 0 && arr[j-1] > value)
decreases j
invariant 0 <= j <= arr.Length
invariant arr[rightIndex+2..] == initialArr[rightIndex+2..]
invariant arr[j+1..rightIndex+2] == initialArr[j..rightIndex+1]
invariant arr[..j] == initialArr[..j]
{
j := j-1;
arr[j + 1] := arr[j];
}
arr[j] := value;
return j;
}
পার্ট ফোর - পিছনের লুপিং সম্পর্কে পরামর্শ
বেশ কয়েক বছর ধরে অনেকগুলি লুপ লিখেছেন এবং প্রমাণ করার পরে, পিছনের দিকে লুপিং সম্পর্কে আমার নীচের সাধারণ পরামর্শ রয়েছে।
হ্রাসটি শেষের পরিবর্তে লুপের শুরুতে সঞ্চালিত হলে পশ্চাৎ (হ্রাস) লুপটি সম্পর্কে চিন্তা করা এবং লিখতে প্রায় সর্বদা সহজ।
দুর্ভাগ্যক্রমে for
অনেক ভাষায় লুপ নির্মাণ এটিকে কঠিন করে তোলে।
আমি সন্দেহ করি (তবে প্রমাণ করতে পারি না) এই জটিলতাটি লুপটি কী হওয়া উচিত এবং এটি আসলে কী হওয়া দরকার সে সম্পর্কে আপনার অন্তর্দৃষ্টিতে পার্থক্য সৃষ্টি করেছিল। আপনি ফরোয়ার্ড (ইনক্রিমেন্টিং) লুপগুলি সম্পর্কে চিন্তাভাবনা করতে অভ্যস্ত। যখন আপনি একটি পশ্চাদপদ (হ্রাসমান) লুপটি লিখতে চান আপনি জিনিসগুলি একটি ফরোয়ার্ড (ইনক্রিমেন্টিং) লুপে ঘটে এমন ক্রমটি বিপরীত করার চেষ্টা করে লুপটি তৈরি করার চেষ্টা করেন। কিন্তু for
কনস্ট্রাক্টটি যেভাবে কাজ করে তার জন্য আপনি অ্যাসাইনমেন্ট এবং লুপ ভেরিয়েবল আপডেটের ক্রমটিকে বিপরীত করতে উপেক্ষা করেছেন - যা একটি পশ্চাৎ এবং সামনের লুপের মধ্যে ক্রিয়াকলাপের সত্যিকার বিপরীতের জন্য প্রয়োজন।
পঞ্চম ভাগ - বোনাস
কেবলমাত্র সম্পূর্ণতার জন্য, আপনি যদি কোডটি rightIndex+1
না দিয়ে পদ্ধতিতে পাস করেন তবে আপনার কোডটি এখানে পাবেন rightIndex
। এই পরিবর্তনগুলি সমস্ত +2
অফসেটগুলি সরিয়ে দেয় যা অন্যথায় লুপটির সঠিকতা সম্পর্কে চিন্তা করতে হবে।
method insert3(arr:array<int>, rightIndex:int, value:int) returns (index:int)
modifies arr
requires arr != null
requires 1 <= rightIndex < arr.Length
ensures 0 <= index <= rightIndex
ensures arr[..index] == old(arr[..index])
ensures arr[index] == value
ensures arr[index+1..rightIndex+1] == old(arr[index..rightIndex])
ensures arr[rightIndex+1..] == old(arr[rightIndex+1..])
{
ghost var initialArr := arr[..];
var j := rightIndex;
while(j > 0 && arr[j-1] > value)
decreases j
invariant 0 <= j <= arr.Length
invariant arr[rightIndex+1..] == initialArr[rightIndex+1..]
invariant arr[j+1..rightIndex+1] == initialArr[j..rightIndex]
invariant arr[..j] == initialArr[..j]
{
j := j-1;
arr[j + 1] := arr[j];
}
arr[j] := value;
return j;
}
j >= 0
এটি একটি ভুল? আপনি যে অ্যাক্সেস করছেনarray[j]
এবংarray[j + 1]
প্রথমে এটি পরীক্ষা না করেই আমি আরও সতর্ক থাকবarray.length > (j + 1)
।