“জ্লিনকিউ”, নেট নেট | এর জন্য একটি শূন্য-বরাদ্দ লিনক লাইব্রেরি লিখেছেন যোশিফুমি কাওয়াই | মে, 2025

“জ্লিনকিউ”, নেট নেট | এর জন্য একটি শূন্য-বরাদ্দ লিনক লাইব্রেরি লিখেছেন যোশিফুমি কাওয়াই | মে, 2025

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

সংজ্ঞা ValueEnumerable<T>যা শৃঙ্খলার ভিত্তি গঠন করে, এটি দেখতে:

public readonly ref struct ValueEnumerable<TEnumerator, T>(TEnumerator enumerator)
where TEnumerator : struct, IValueEnumerator<T>, allows ref struct // allows ref struct only in .NET 9 or later

public readonly TEnumerator Enumerator = enumerator;

public interface IValueEnumerator<T> : IDisposable

bool TryGetNext(out T current); // as MoveNext + Current
// Optimization helper
bool TryGetNonEnumeratedCount(out int count);
bool TryGetSpan(out ReadOnlySpan<T> span);
bool TryCopyTo(scoped Span<T> destination, Index offset);

এর উপর ভিত্তি করে, অপারেটররা যেখানে নীচের হিসাবে চেইন:

public static ValueEnumerable<Where<TEnumerator, TSource>, TSource> Where<TEnumerator, TSource>(this ValueEnumerable<TEnumerator, TSource> source, Func<TSource, Boolean> predicate)
where TEnumerator : struct, IValueEnumerator<TSource>, allows ref struct

আমরা ব্যবহারের চেয়ে এই পদ্ধতির বেছে নিয়েছি IValueEnumerable<T> কারণ একটি সংজ্ঞা মত (this TEnumerable source) where TEnumerable : struct, IValueEnumerable<TSource>জন্য অনুমান টাইপ TSource ব্যর্থ হবে। এটি একটি সি# ভাষার সীমাবদ্ধতার কারণে যেখানে টাইপ ইনফারেন্স টাইপ প্যারামিটার সীমাবদ্ধতা থেকে কাজ করে না (ডটনেট/চার্পল্যাং#6930)। যদি সেই সংজ্ঞা দিয়ে প্রয়োগ করা হয় তবে এটির জন্য প্রচুর সংখ্যক সংমিশ্রণের জন্য উদাহরণ পদ্ধতিগুলি সংজ্ঞায়িত করা দরকার। লিঙ্গফ সেই পদ্ধতির গ্রহণ, ফলস্বরূপ 100,000+ পদ্ধতি এবং বিশাল সমাবেশের আকারযা আদর্শ ছিল না।

লিনকিউতে, সমস্ত বাস্তবায়ন রয়েছে IValueEnumerator<T>এবং যেহেতু সমস্ত গণক স্ট্রাক্ট, তাই আমি বুঝতে পেরেছিলাম যে এটি ব্যবহারের পরিবর্তে GetEnumerator()আমরা কেবল কমনটি অনুলিপি করতে পারি Enumeratorপ্রতিটি গণককে তার স্বাধীন রাষ্ট্রের সাথে প্রক্রিয়া করার অনুমতি দেয়। এটি মোড়কের চূড়ান্ত কাঠামোর দিকে পরিচালিত করে IValueEnumerator<T> সঙ্গে ValueEnumerable<TEnumerator, T>। এইভাবে, প্রকারগুলি সীমাবদ্ধতার চেয়ে টাইপ ঘোষণায় উপস্থিত হয়, প্রকারের অনুমানের সমস্যাগুলি এড়ানো।

আসুন আরও বিশদে, পুনরাবৃত্তির মূল মুভেনেক্সট পরীক্ষা করি:

// Traditional interface
public interface IEnumerator<out T> : IDisposable

bool MoveNext();
T Current get;

// iterate example
while (e.MoveNext())

var item = e.Current; // invoke get_Current()

// ZLinq interface
public interface IValueEnumerator<T> : IDisposable

bool TryGetNext(out T current);

// iterate example
while (e.TryGetNext(out var item))

সি#’গুলি foreach প্রসারিত MoveNext() + Currentযা দুটি বিষয় উপস্থাপন করে। প্রথমত, প্রতিটি পুনরাবৃত্তির জন্য দুটি পদ্ধতি কল প্রয়োজন: মুভেনেক্সট এবং get_current। দ্বিতীয়ত, কারেন্টের একটি ভেরিয়েবল রাখা প্রয়োজন। অতএব, আমি তাদের মধ্যে একত্রিত bool TryGetNext(out T current)। এটি কার্যকারিতা উন্নত করে, প্রতি পুনরাবৃত্তিতে একটিতে পদ্ধতি কলগুলি হ্রাস করে।

এই bool TryGetNext(out T current) পদ্ধতির মধ্যেও ব্যবহৃত হয় জং এর পুনরাবৃত্তি::

pub trait Iterator 
type Item;
// Required method
fn next(&mut self) -> Option<Self::Item>;

ভেরিয়েবল হোল্ডিং ইস্যুটি বুঝতে, আসুন নির্বাচন করা বাস্তবায়নটি দেখুন:

public sealed class LinqSelect<TSource, TResult>(IEnumerator<TSource> source, Func<TSource, TResult> selector) : IEnumerator<TResult>

// Three fields
IEnumerator<TSource> source = source;
Func<TSource, TResult> selector = selector;
TResult current = default!;

public TResult Current => current;

public bool MoveNext()

if (source.MoveNext())

current = selector(source.Current);
return true;

return false;

public ref struct ZLinqSelect<TEnumerator, TSource, TResult>(TEnumerator source, Func<TSource, TResult> selector) : IValueEnumerator<TResult>
where TEnumerator : struct, IValueEnumerator<TSource>, allows ref struct

// Two fields
TEnumerator source = source;
Func<TSource, TResult> selector = selector;
public bool TryGetNext(out TResult current)

if (source.TryGetNext(out var value))

current = selector(value);
return true;

current = default!;
return false;

IEnumerator<T> একটি প্রয়োজন একটি current ক্ষেত্র কারণ এটি সঙ্গে অগ্রগতি MoveNext() এবং সাথে ফিরে Current। যাইহোক, জ্লিনকিউ ক্ষেত্রটি সঞ্চয় করার প্রয়োজনীয়তা দূর করে একযোগে মানগুলি অগ্রসর করে এবং ফেরত দেয়। এটি জ্লিনকিউর স্ট্রাক্ট-ভিত্তিক আর্কিটেকচারে একটি গুরুত্বপূর্ণ পার্থক্য তৈরি করে। যেহেতু zlinq এমন একটি কাঠামো আলিঙ্গন করে যেখানে প্রতিটি পদ্ধতি চেইন সম্পূর্ণরূপে অন্তর্ভুক্ত থাকে (TEnumerator একটি কাঠামো হওয়ায়), প্রতিটি পদ্ধতি চেইনের সাথে স্ট্রাক্ট আকার বৃদ্ধি পায়। যদিও পারফরম্যান্স যুক্তিসঙ্গত পদ্ধতি চেইনের দৈর্ঘ্যের মধ্যে গ্রহণযোগ্য থেকে যায়, ছোট স্ট্রাক্টগুলির অর্থ কম অনুলিপি ব্যয় এবং আরও ভাল পারফরম্যান্স। দত্তক গ্রহণ TryGetNext স্ট্রাক্ট আকার হ্রাস করার জন্য প্রয়োজনীয় ছিল।

ট্রাইগেটনেক্সট এর একটি অপূর্ণতা হ’ল এটি কোভেরিয়েন্স এবং বিরোধকে সমর্থন করতে পারে না। যাইহোক, আমি বিশ্বাস করি যে পুনরাবৃত্তকারী এবং অ্যারেগুলি কোভারিয়েন্স/অবরুদ্ধতা সমর্থন পুরোপুরি ত্যাগ করা উচিত। তারা বেমানান Span<T>উপকারিতা এবং কনস ওজন করার সময় তাদের পুরানো ধারণাগুলি তৈরি করা। উদাহরণস্বরূপ, অ্যারে স্প্যান রূপান্তর সংকলন-সময় সনাক্তকরণ ছাড়াই রানটাইমে ব্যর্থ হতে পারে:

// Due to generic variance, Derived() is accepted by Base()
Base() array = new Derived() new Derived(), new Derived() ;

// In this case, casting to Span<T> or using AsSpan() causes a runtime error!
// System.ArrayTypeMismatchException: Attempted to access an element as a type incompatible with the array.
Span<Base> foo = array;
class Base;
class Derived : Base;

যদিও এই আচরণটি বিদ্যমান কারণ এই বৈশিষ্ট্যগুলি আগে যুক্ত করা হয়েছিল Span<T>এটি আধুনিক। নেট যেখানে স্প্যান ব্যাপকভাবে ব্যবহৃত হয় সেখানে সমস্যাযুক্ত, এমন বৈশিষ্ট্য তৈরি করে যা রানটাইম ত্রুটিগুলি ব্যবহারিকভাবে অকেজো করতে পারে।

নির্লজ্জভাবে সমস্ত কিছু গণনা করা কর্মক্ষমতা সর্বাধিক করে না। উদাহরণস্বরূপ, টোয়ারে কল করার সময়, যদি আকার পরিবর্তন না হয় (যেমন, array.Select().ToArray()), আমরা সাথে একটি স্থির দৈর্ঘ্যের অ্যারে তৈরি করতে পারি new T(count)। সিস্টেম.লিনকিউ অভ্যন্তরীণভাবে একটি ব্যবহার করে Iterator<T> এই জাতীয় অপ্টিমাইজেশনের জন্য টাইপ করুন, তবে যেহেতু প্যারামিটারটি রয়েছে IEnumerable<T>কোড পছন্দ if (source is Iterator<TSource> iterator) সর্বদা প্রয়োজন।

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

TryGetNonEnumeratedCount(out int count) সফল হয় যখন মূল উত্সটির একটি সীমাবদ্ধ গণনা থাকে এবং কোনও ফিল্টারিং পদ্ধতি থাকে না (যেখানে, স্বতন্ত্র ইত্যাদি, যদিও নেওয়া এবং এড়িয়ে যাওয়া গণনাযোগ্য) হস্তক্ষেপ করে। এটি অর্ডারবাই এবং শ্যাফলের মতো মধ্যবর্তী বাফারদের প্রয়োজনীয় ট্যারে এবং পদ্ধতিগুলি উপকৃত করে।

TryGetSpan(out ReadOnlySpan<T> span) যখন উত্সটি সংলগ্ন মেমরি হিসাবে অ্যাক্সেস করা যায়, সিমডি অপারেশন বা সংহতকরণের পারফরম্যান্সের জন্য স্প্যান-ভিত্তিক লুপ প্রসেসিং সক্ষম করে তখন সম্ভাব্যভাবে নাটকীয় পারফরম্যান্সের উন্নতিগুলি সরবরাহ করে।

TryCopyTo(scoped Span<T> destination, Index offset) অভ্যন্তরীণ পুনরাবৃত্তির মাধ্যমে কর্মক্ষমতা বাড়ায়। বাহ্যিক বনাম অভ্যন্তরীণ পুনরাবৃত্তিগুলি ব্যাখ্যা করতে, এটি বিবেচনা করুন List<T> উভয় অফার foreach এবং ForEach::

// external iterator
foreach (var item in list) Do(item);

// internal iterator
list.ForEach(Do);

তারা দেখতে একই রকম তবে আলাদাভাবে সম্পাদন করে। বাস্তবায়ন ভেঙে:

// external iterator
List<T>.Enumerator e = list.GetEnumerator();
while (e.MoveNext())

var item = e.Current;
Do(item);

// internal iterator
for (int i = 0; i < _size; i++)

action(_items(i));

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

অবশ্যই, এটি কেস অনুসারে ক্ষেত্রে পরিবর্তিত হয় এবং যেহেতু ল্যাম্বদা ক্যাপচার এবং সাধারণ নিয়ন্ত্রণ প্রবাহ (যেমন চালিয়ে যান, বিরতি, অপেক্ষা করা ইত্যাদি…) উপলভ্য নয়, তাই আমি ব্যক্তিগতভাবে বিশ্বাস করি ForEach ব্যবহার করা উচিত নয়, বা কাস্টম এক্সটেনশন পদ্ধতিগুলি এটি নকল করার জন্য সংজ্ঞায়িত করা উচিত নয়। তবে এই কাঠামোগত পার্থক্য বিদ্যমান।

TryCopyTo(scoped Span<T> destination, Index offset) একটি গ্রহণ করে সীমিত অভ্যন্তরীণ পুনরাবৃত্তি অর্জন Span বরং একটি প্রতিনিধি।

উদাহরণ হিসাবে নির্বাচন করে ব্যবহার করে, ট্যারেয়ের জন্য যখন গণনা পাওয়া যায়, এটি অভ্যন্তরীণ পুনরাবৃত্তির জন্য একটি স্প্যান পাস করে:

public ref struct Select
{
public bool TryCopyTo(Span<TResult> destination, Index offset)

if (source.TryGetSpan(out var span))

if (EnumeratorHelper.TryGetSlice(span, offset, destination.Length, out var slice))

// loop inlining
for (var i = 0; i < slice.Length; i++)

destination(i) = selector(slice(i));

return true;


return false;

}

// ------------------

// ToArray
if (enumerator.TryGetNonEnumeratedCount(out var count))

var array = GC.AllocateUninitializedArray<TSource>(count);
// try internal iterator
if (enumerator.TryCopyTo(array.AsSpan(), 0))

return array;

// otherwise, use external iterator
var i = 0;
while (enumerator.TryGetNext(out var item))

array(i) = item;
i++;

return array;

সুতরাং, যদিও নির্বাচনটি কোনও স্প্যান তৈরি করতে পারে না, যদি মূল উত্সটি পারে তবে অভ্যন্তরীণ পুনরাবৃত্তকারী হিসাবে প্রক্রিয়াজাতকরণ লুপ প্রসেসিংকে ত্বরান্বিত করে।

TryCopyTo নিয়মিত থেকে পৃথক CopyTo একটি দ্বারা একটি Index offset এবং গন্তব্য উত্সের চেয়ে ছোট হতে দেয় (সাধারণ .NET কপিটো ব্যর্থ হয় যদি গন্তব্যটি ছোট হয়)। গন্তব্য আকার 1 হয় যখন এটি প্রাথমিক প্রতিনিধিত্ব সক্ষম করে – সূচক 0 প্রথম হয়, ^1 শেষ হয়। যোগ করা First, Last, ElementAt সরাসরি IValueEnumerator<T> শ্রেণীর সংজ্ঞাগুলিতে (সমাবেশের আকারকে প্রভাবিত করে) অপ্রয়োজনীয়তা তৈরি করবে, তবে সূচকগুলির সাথে ছোট গন্তব্যগুলির সংমিশ্রণে একটি পদ্ধতিকে আরও অপ্টিমাইজেশনের ক্ষেত্রে কভার করার অনুমতি দেয়:

public static TSource ElementAt<TEnumerator, TSource>(this ValueEnumerable<TEnumerator, TSource> source, Index index)
where TEnumerator : struct, IValueEnumerator<TSource>, allows ref struct

using var enumerator = source.Enumerator;
var value = default(TSource)!;
var span = new Span<T>(ref value); // create single span
if (enumerator.TryCopyTo(span, index))

return value;

// else...

নেট 9 বা তারপরে, জ্লিনকিউ সমস্ত লিনকিউ অপারেটরকে চেইন করার অনুমতি দেয় Span<T> এবং ReadOnlySpan<T>::

using ZLinq;

// Can also be applied to Span (only in .NET 9/C# 13 environments that support allows ref struct)
Span<int> span = stackalloc int(5) 1, 2, 3, 4, 5 ;
var seq1 = span.AsValueEnumerable().Select(x => x * x);
// If enables Drop-in replacement, you can call LINQ operator directly.
var seq2 = span.Select(x => x);

কিছু লাইব্রেরি স্প্যানগুলির জন্য লিনকিউকে সমর্থন করার দাবি করে, তারা সাধারণত কেবলমাত্র এক্সটেনশন পদ্ধতিগুলি সংজ্ঞায়িত করে Span<T> জেনেরিক প্রক্রিয়া ছাড়া। তারা ভাষা সীমাবদ্ধতার কারণে সীমিত অপারেটর সরবরাহ করে যা পূর্বে প্রাপ্তি প্রতিরোধ করেছিল Span<T> জেনেরিক প্যারামিটার হিসাবে। জেনেরিক প্রসেসিং প্রবর্তনের সাথে সম্ভব হয়েছিল allows ref struct ইন। নেট 9।

জ্লিনকিউতে, এর মধ্যে কোনও পার্থক্য নেই IEnumerable<T> এবং Span<T> – তাদের সমান আচরণ করা হয়েছে।

তবে, যেহেতু allows ref struct ভাষা/রানটাইম সমর্থন প্রয়োজন, যখন জ্লিনকিউ। নেট স্ট্যান্ডার্ড ২.০ ইউপি থেকে সমস্ত .NET সংস্করণ সমর্থন করে, স্প্যান সমর্থনটি নেট 9 এবং তারও বেশি সীমাবদ্ধ। এর অর্থ। নেট 9+, সমস্ত অপারেটরগুলি হ’ল ref structযা পূর্ববর্তী সংস্করণ থেকে পৃথক।

System.linq সিমডের সাথে নির্দিষ্ট সমষ্টি পদ্ধতিগুলি ত্বরান্বিত করে। উদাহরণস্বরূপ, আদিম ধরণের অ্যারেগুলিতে সরাসরি যোগ বা সর্বোচ্চ কল করা লুপের জন্য ব্যবহারের চেয়ে দ্রুত প্রসেসিং সরবরাহ করে। তবে, ভিত্তিক হচ্ছে IEnumerable<T>প্রযোজ্য প্রকারগুলি সীমাবদ্ধ। জ্লিনকিউ এটিকে আরও জেনেরিক করে তোলে IValueEnumerator.TryGetSpanসংগ্রহগুলি লক্ষ্য করা যেখানে Span<T> প্রাপ্ত করা যেতে পারে (সরাসরি সহ) Span<T> আবেদন)।

সমর্থিত পদ্ধতিগুলির মধ্যে রয়েছে:

  • পরিসীমা ট্যারে/টোলিস্ট/কপিরো/ইত্যাদি…
  • পুনরাবৃত্তি জন্য unmanaged struct এবং size is power of 2 ট্যারে/টোলিস্ট/কপিরো/ইত্যাদি …
  • যোগফল জন্য sbyte, short, int, long, byte, ushort, uint, ulong, double
  • Sumunched জন্য sbyte, short, int, long, byte, ushort, uint, ulong, double
  • গড় জন্য sbyte, short, int, long, byte, ushort, uint, ulong, double
  • সর্বোচ্চ জন্য byte, sbyte, short, ushort, int, uint, long, ulong, nint, nuint, Int128, UInt128
  • মিনিট জন্য byte, sbyte, short, ushort, int, uint, long, ulong, nint, nuint, Int128, UInt128
  • রয়েছে জন্য byte, sbyte, short, ushort, int, uint, long, ulong, bool, char, nint, nuint
  • সিকোয়েন্সিক জন্য byte, sbyte, short, ushort, int, uint, long, ulong, bool, char, nint, nuint

Sum ওভারফ্লোয়ের জন্য চেকগুলি, যা ওভারহেড যুক্ত করে। আমরা একটি কাস্টম যুক্ত করেছি SumUnchecked পদ্ধতি যে দ্রুত:

যেহেতু শর্তগুলি মেলে এই পদ্ধতিগুলি সুস্পষ্টভাবে প্রয়োগ করা হয়, তাই অভ্যন্তরীণ পাইপলাইন বোঝা সিমডি অ্যাপ্লিকেশনটিকে লক্ষ্য করার জন্য প্রয়োজনীয়। সুতরাং, জন্য T(), Span<T>বা ReadOnlySpan<T>আমরা সরবরাহ করি .AsVectorizable() স্পষ্টভাবে সিমডি-আবেদনযোগ্য অপারেশনগুলিকে কল করার পদ্ধতি Sum, SumUnchecked, Average, Max, Min, Containsএবং SequenceEqual (যদিও এগুলি স্বাভাবিক প্রক্রিয়াকরণে ফিরে আসে Vector.IsHardwareAccelerated && Vector<T>.IsSupported মিথ্যা)।

int() বা Span<int> লাভ VectorizedFillRange পদ্ধতি, যা একই অপারেশন সম্পাদন করে ValueEunmerable.Range().CopyTo()সিমডি ত্বরণ ব্যবহার করে ক্রমিক সংখ্যাগুলি পূরণ করা। এটি যখন প্রয়োজন হয় তখন লুপের সাথে পূরণ করার চেয়ে এটি আরও দ্রুত:

হস্তাক্ষর সিমডি লুপ প্রসেসিংয়ের অনুশীলন এবং প্রচেষ্টা প্রয়োজন। আমরা এমন সাহায্যকারীদের সরবরাহ করেছি যা নৈমিত্তিক ব্যবহারের জন্য মজাদার যুক্তি নেয়। যদিও এই ইনসুর ওভারহেডের প্রতিনিধি এবং ইনলাইন কোডের চেয়ে খারাপ পারফর্ম করে, তারা নৈমিত্তিক সিমডি প্রসেসিংয়ের জন্য সুবিধাজনক। তারা গ্রহণ করে Func<Vector<T>, Vector<T>> vectorFunc এবং Func<T, T> funcসঙ্গে প্রক্রিয়াজাতকরণ Vector<T> যেখানে সম্ভব এবং হ্যান্ডলিং বাকি Func<T>

T() এবং Span<T> অফার VectorizedUpdate পদ্ধতি:

using ZLinq.Simd; // needs using

int() source = Enumerable.Range(0, 10000).ToArray();
(Benchmark)
public void For()

for (int i = 0; i < source.Length; i++)

source(i) = source(i) * 10;


(Benchmark)
public void VectorizedUpdate()

// arg1: Vector<int> => Vector<int>
// arg2: int => int
source.VectorizedUpdate(static x => x * 10, static x => x * 10);

লুপগুলির চেয়ে দ্রুততর হলেও, পারফরম্যান্স মেশিনের পরিবেশ এবং আকারের দ্বারা পরিবর্তিত হয়, তাই প্রতিটি ব্যবহারের ক্ষেত্রে যাচাইয়ের পরামর্শ দেওয়া হয়।

AsVectorizable() সরবরাহ করে Aggregate, All, Any, Count, Selectএবং Zip::

source.AsVectorizable().Aggregate((x, y) => Vector.Min(x, y), (x, y) => Math.Min(x, y))
source.AsVectorizable().All(x => Vector.GreaterThanAll(x, new(5000)), x => x > 5000);
source.AsVectorizable().Any(x => Vector.LessThanAll(x, new(5000)), x => x < 5000);
source.AsVectorizable().Count(x => Vector.GreaterThan(x, new(5000)), x => x > 5000);

পারফরম্যান্স ডেটার উপর নির্ভর করে, তবে গণনা উল্লেখযোগ্য পার্থক্য দেখাতে পারে:

জন্য Select এবং Zipআপনি উভয় সঙ্গে অনুসরণ ToArray বা CopyTo::

// Select
source.AsVectorizable().Select(x => x * 3, x => x * 3).ToArray();
source.AsVectorizable().Select(x => x * 3, x => x * 3).CopyTo(destination);

// Zip2
array1.AsVectorizable().Zip(array2, (x, y) => x + y, (x, y) => x + y).CopyTo(destination);
array1.AsVectorizable().Zip(array2, (x, y) => x + y, (x, y) => x + y).ToArray();
// Zip3
array1.AsVectorizable().Zip(array2, array3, (x, y, z) => x + y + z, (x, y, z) => x + y + z).CopyTo(destination);
array1.AsVectorizable().Zip(array2, array3, (x, y, z) => x + y + z, (x, y, z) => x + y + z).ToArray();

নির্দিষ্ট ব্যবহারের ক্ষেত্রে জিপ বিশেষভাবে আকর্ষণীয় এবং দ্রুত হতে পারে (দুটি ভেক 3 মার্জ করার মতো):

আপনি কি এক্সএমএল থেকে লিনক ব্যবহার করেছেন? ২০০৮ সালে যখন লিনকিউ উপস্থিত হয়েছিল, এক্সএমএল তখনও প্রভাবশালী ছিল এবং এক্সএমএল এর ব্যবহারযোগ্যতার জন্য লিনকিউ হতবাক ছিল। এখন যে জেএসএন দখল করেছে, লিনক থেকে এক্সএমএলে খুব কমই ব্যবহৃত হয়।

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

জ্লিনকিউ এমন একটি ইন্টারফেস সংজ্ঞায়িত করে এই ধারণাটি প্রসারিত করে যা সাধারণভাবে সক্ষম করে Ancestors, Children, Descendants, BeforeSelfএবং AfterSelf গাছের কাঠামোর জন্য:

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

public interface ITraverser<TTraverser, T> : IDisposable
where TTraverser : struct, ITraverser<TTraverser, T> // self

T Origin get;
TTraverser ConvertToTraverser(T next); // for Descendants
bool TryGetHasChild(out bool hasChild); // optional: optimize use for Descendants
bool TryGetChildCount(out int count); // optional: optimize use for Children
bool TryGetParent(out T parent); // for Ancestors
bool TryGetNextChild(out T child); // for Children

জসনের জন্য, আপনি লিখতে পারেন:

var json = JsonNode.Parse("""
// snip...
""");

// JsonNode
var origin = json!("nesting")!("level1")!("level2")!;
// JsonNode axis, Children, Descendants, Anestors, BeforeSelf, AfterSelf and ***Self.
foreach (var item in origin.Descendants().Select(x => x.Node).OfType<JsonArray>())

// (true, false, true), ("fast", "accurate", "balanced"), (1, 1, 2, 3, 5, 8, 13)
Console.WriteLine(item.ToJsonString(JsonSerializerOptions.Web));

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

Source link