سوف يركز هذا المنشور على الميزة الرئيسية ، في رأيي ، للغة جوليا - عرض الوظائف في شكل طرق متعددة الإرسال. يتيح لك ذلك زيادة أداء العمليات الحسابية دون التقليل من قابلية قراءة الشفرة ودون إفساد القابلية للتجريد ، من ناحية ، ويسمح لك بالعمل مع المفاهيم الرياضية في تدوين أكثر دراية ، من ناحية أخرى. على سبيل المثال ، مسألة الزي الموحد (من وجهة نظر العمليات الخطية) تعمل مع كثير الحدود في تمثيل قائمة المعاملات ومع متعددات الحدود الداخلية.
بناء الجملة الأساسية
مقدمة موجزة لأولئك الذين ليسوا في معرفة. جوليا هي لغة شبيهة بالنص ، ولديها REPL (حلقة قراءة-قراءة-طباعة ، أي غلاف تفاعلي). للوهلة الأولى يبدو مشابهاً جداً ، على سبيل المثال ، لبيثون أو ماتلاب.
العمليات الحسابية
الحساب هو نفسه تقريبا في كل مكان: + ، - ، * ، / ، ^ ، للتسفي ، الخ
المقارنة:> ، <،> = ، <= ، == ،! = إلخ.
الواجب: =.
الميزات: القسمة من خلال
/
دائما إرجاع عدد كسور. إذا كنت بحاجة إلى الجزء الصحيح من قسم عدد صحيحين ، فأنت بحاجة إلى استخدام
div(m, n)
أو مكافئ infix
m ÷ n
.
أنواع
أنواع رقمية:
- أعداد صحيحة (
Int
) - 2
، 3
، -42
- أعداد صحيحة غير موقعة (
UInt
) - 0x12345
- النقطة العائمة (
Float32
، Float64
) - 1.0
، 3.1415
، -Inf
، NaN
- عقلاني (
Rational
) - 3//3
، 7//2
- حقيقي (
Real
) - كل ما سبق - Complex (
Complex
) - 3+4*im
، 2//3+2//3*im
، 3.0+0.0*im
( im
هي وحدة وهمية ، فقط رقم به جزء تخيلي مكتوب بشكل صريح يعتبر معقدًا) Number
- كل ما سبق
السلاسل والشخصيات:
'a'
- شخصية ( Char
)"a"
عبارة عن سلسلة ( String
)
ملحوظة: السلاسل ، كما هو الحال الآن في العديد من اللغات ، غير قابلة للتغيير.
ملحوظة: تدعم السلاسل (وكذلك أسماء المتغيرات) Unicode ، بما في ذلك الرموز التعبيرية.
المصفوفات:
x = [1, 2, 3]
- تحديد مجموعة من التعداد المباشر للعناصرzeros(length)
الخاصة: zeros(length)
لمجموعة من الأصفار ، الأصفار ones(length)
لمجموعة من الأعمدة ، rand(length)
لمجموعة من الأرقام العشوائية ، إلخ.- دعم مجموعة متعددة الأبعاد
- دعم عمليات الجبر الخطي (إضافة المصفوفات ، الضرب القياسي ، ضرب مصفوفة المتجهات ، وأكثر من ذلك بكثير) في المكتبة القياسية
ملحوظة: يتم فهرسة جميع المجموعات بدءًا من واحدة.
ملحوظة: لأن اللغة مخصصة للمهام الحسابية ، والمصفوفات هي واحدة من أهم الأنواع ، وسوف تضطر إلى العودة إلى مبادئ عملها أكثر من مرة.
Tuples (مجموعة من العناصر مرتبة ، غير قابلة للتغيير):
(2, 5.3, "k")
هو tuple منتظم(a = 3, b = 4)
- الاسم المسمى tuple
ملحوظة: يمكن الوصول إلى حقول tuple المسماة بالاسم خلال فترة وحسب الفهرس عبر []
julia> x = (a = 5, b = 12) (a = 5, b = 12) julia> x[1] 5 julia> sqrt(xa^2 + x[2]^2) 13.0
القواميس:
julia> x = Dict('a' => 5, 'b' => 12) Dict{Char,Int64} with 2 entries: 'a' => 5 'b' => 12 julia> x['c'] = 13 13 julia> x Dict{Char,Int64} with 3 entries: 'a' => 5 'c' => 13 'b' => 12
بنيات لغة التحكم الأساسية
1. يتم إنشاء المتغيرات تلقائيا عند التعيين. النوع اختياري.
julia> x = 7; x + 2 9 julia> x = 42.0; x * 4 168.0
2. كتلة الانتقال الشرطية تبدأ بالتعبير
if <condition>
وتنتهي
end
الكلمة. يمكنك أيضًا الحصول على أضواء
elseif
أو أضواء أخرى:
if x > y println("X is more than Y") elseif x == y println("X and Y are equal") else println("X is less than Y") end
3. هناك اثنين من يبني حلقة: في
while
for
. والثاني يعمل كما في بيثون ، أي يتكرر على المجموعة. الاستخدام الشائع هو التكرار على نطاق من القيم التي
start[:increment]:end
بناء الجملة فيها
start[:increment]:end
. على عكس بيثون ،
يشتمل النطاق على قيمتي البداية والنهاية ، أي لن يكون النطاق الفارغ
1:1
(هذا هو نطاق 1) ، ولكن
1:0
. يتم وضع علامة
end
جسم الحلقة
end
الكلمة.
julia> for i in 1:3; print(i, " "); end
4. يتم تحديد وظائف من خلال
function
الكلمة الأساسية ، كما ينتهي تعريف الوظيفة
end
الكلمة. يتم دعم الوسائط ذات القيم الافتراضية والوسائط المسماة.
function square(x) return x * x end function cube(x) x * square(x)
بشكل عام ، كل هذا مشابه تمامًا لـ Python ، باستثناء الاختلافات الطفيفة في بناء الجملة وحقيقة أن الكود من التعليمات البرمجية لا يتم تخصيصها بمسافات ، ولكن لا يزال مع الكلمات الأساسية. في الحالات البسيطة ، تتحول برامج بايثون إلى جوليا من واحد إلى واحد تقريبًا.
ولكن هناك اختلاف كبير في حقيقة أنه في جوليا يمكنك تحديد أنواع المتغيرات بشكل صريح ، والتي تتيح لك ترجمة البرامج والحصول على رمز سريع.
الفارق الثاني المهم هو أن بيثون تنفذ نموذج OOP "الكلاسيكي" مع الطبقات والأساليب ، في حين تنفذ جوليا نموذجًا متعدد الإرسال.
اكتب التعليقات التوضيحية والإرسال المتعدد
دعونا نرى ما هي وظيفة المدمج في:
julia> sqrt sqrt (generic function with 19 methods)
كما يوضح لنا REPL ،
sqrt
هي وظيفة عامة مع 19 طريقة. أي نوع من الوظائف المعممة وأي نوع من الأساليب؟
وهذا يعني أن هناك
العديد من وظائف
sqrt
التي تنطبق على أنواع مختلفة من الوسائط ، وبالتالي ، قم بحساب الجذر التربيعي باستخدام خوارزميات مختلفة. يمكنك معرفة الخيارات المتاحة عن طريق الكتابة
julia> methods(sqrt)
يمكن ملاحظة أن الوظيفة محددة لأنواع مختلفة من الأرقام ، وكذلك للمصفوفات.
على عكس OOP "الكلاسيكية" ، حيث يتم تحديد التنفيذ الملموس للأسلوب فقط من خلال فئة الاستدعاء (الإرسال بواسطة الوسيطة الأولى) ، في جوليا يتم تحديد اختيار الوظيفة حسب أنواع (وعدد)
جميع الوسائط الخاصة بها.
عند استدعاء دالة ذات وسيطات محددة من جميع أساليبها ، يتم تحديد واحدة تصف بدقة أكثر مجموعة محددة من الأنواع التي تسمى الوظيفة ، وهي التي يتم استخدامها.
السمة المميزة هي أنه يتم تطبيق نهج يسمى "التجميع في وقت مبكر" من قبل مؤلفي اللغة. أي يتم تجميع الوظائف لأنواع البيانات المحددة في المكالمة الأولى ، وبعد ذلك يتم إجراء المكالمات التالية بشكل أسرع بكثير. يمكن أن يكون الفرق بين المكالمات الأولى والمكالمات اللاحقة كبيرًا جدًا:
julia> @time sqrt(8)
في الحالة السيئة ، كل استدعاء دالة هو فحص لنوع الوسائط المستلمة والبحث عن الطريقة المطلوبة في القائمة. ومع ذلك ، إذا قمت بإعطاء تلميحات إلى برنامج التحويل البرمجي ، فيمكنك إزالة عمليات الفحص ، مما يؤدي إلى رمز أسرع.
على سبيل المثال ، ضع في اعتبارك حساب المبلغ
function mysqrt(num)
يوضح المعيار أن وظيفة
S_typed()
لا تعمل فقط بشكل أسرع ، ولكن لا تتطلب أيضًا تخصيص ذاكرة لكل مكالمة ، على عكس
S()
. المشكلة هنا هي أن نوع
mysqrt()
إرجاعها من
mysqrt()
غير معرف ، تمامًا مثل نوع الجانب الأيمن من التعبير
sum = sum + mysqrt(sgn)
نتيجة لذلك ، لا يمكن للمترجم معرفة نوع
sum
سيكون في كل تكرار. لذلك ، فإن الملاكمة (كتابة تسمية النوع) هي متغير ويتم تخصيص الذاكرة.
بالنسبة إلى
S_typed()
، يعرف المترجم مقدمًا أن
sum
هو قيمة معقدة ، وبالتالي فإن الكود محسّن أكثر (على وجه الخصوص ، استدعاء
mysqrt()
يمكن أن يكون مضمنًا بشكل فعال ،
mysqrt()
دائمًا قيمة الإرجاع إلى
Complex
).
الأهم من ذلك ، بالنسبة إلى
S_typed()
يعرف المترجم أن قيمة الإرجاع هي من النوع
Complex
، ولكن بالنسبة إلى
S()
نوع قيمة الإخراج مرة أخرى ، مما سيؤدي إلى إبطاء جميع الوظائف التي سيتم استدعاء
S()
.
يمكنك التحقق من أن المحول البرمجي يفكر في الأنواع التي تم إرجاعها من التعبير باستخدام الماكرو
@code_warntype
:
julia> @code_warntype S(3) Body::Any
إذا تم استدعاء وظيفة في مكان ما في الحلقة التي لا تستطيع
@code_warntype
طباعة نوع الإرجاع ، أو التي تظهر في مكان ما في الجسم استلام قيمة النوع "
Any
، فمن المحتمل أن يوفر تحسين هذه المكالمات تعزيزًا ملموسًا للأداء.
أنواع المركبات
يمكن للمبرمج تحديد أنواع البيانات المركبة لاحتياجاته باستخدام
struct
الهيكلية:
julia> struct GenericStruct
الهياكل في جوليا غير قابلة للتغيير ، أي عن طريق إنشاء مثيل للبنية ، لم يعد من الممكن تغيير قيم الحقول (بتعبير أدق ، لا يمكنك تغيير عنوان الحقول في الذاكرة - يمكن تغيير عناصر الحقول القابلة للتغيير ، مثل
sv
في المثال أعلاه). يتم إنشاء بنيات
mutable struct
، يكون بناء جملةها مماثلًا للهياكل العادية.
لا يتم دعم وراثة الهياكل بالمعنى "الكلاسيكي" ، ولكن هناك احتمال "وراثة" السلوك من خلال الجمع بين الأنواع المركبة في أنواع فائقة أو ، كما يطلق عليها في جوليا ، أنواع مجردة. يتم التعبير عن علاقات الكتابة كـ
A<:B
(A هو نوع فرعي من B) و
A>:B
(A هو نوع فرعي من B). يبدو شيء مثل هذا:
abstract type NDimPoint end
دراسة حالة: كثيرات الحدود
نظام الكتابة إلى جانب إرسال متعددة مناسب للتعبير عن المفاهيم الرياضية. دعونا نلقي نظرة على مثال لمكتبة بسيطة للعمل مع كثير الحدود.
نقدم نوعين من كثيرات الحدود: "متعارف عليه" ، يتم تعريفه من خلال المعاملات عند القوى ، و "الاستيفاء" ، المعرف بمجموعة من الأزواج (x ، f (x)). للبساطة ، سننظر في الحجج الصحيحة فقط.
لتخزين كثير الحدود في تدوين معتاد ، يكون الهيكل الذي يحتوي على صفيف أو مجموعة من المعاملات كحقل مناسبًا. لتكون غير قابل للتغيير بالكامل ، فليكن هناك موكب. وهكذا ، فإن الكود الخاص بتعريف النوع التجريدي وبنية كثير الحدود وحساب قيمة كثير الحدود في نقطة معينة بسيط للغاية:
abstract type AbstractPolynomial end """ Polynomial <: AbstractPolynomial Polynomials written in the canonical form """ struct Polynomial<:AbstractPolynomial degree::Int coeff::NTuple{N, Float64} where N
تحتاج كثيرات الحدود الداخلية إلى بنية تمثيل وطريقة حساب مختلفة. على وجه الخصوص ، إذا كانت مجموعة نقاط الاستيفاء معروفة مسبقًا ، وكان من المخطط حساب نفس الحدود المتعددة في نقاط مختلفة ،
تكون صيغة الاستيفاء التي وضعها
نيوتن ملائمة:
حيث
n k (
x ) متعددات الحدود الأساسية ،
n 0 (
x ) و
k > 0
حيث
x i هي عقد الاستيفاء.
من الصيغ أعلاه ، يمكن ملاحظة أن التخزين منظم بشكل ملائم في شكل مجموعة من العقد الاستيفائية
x i ومعاملات
c i ، ويمكن إجراء الحساب بطريقة مشابهة لمخطط Horner.
""" InterpPolynomial <: AbstractPolynomial Interpolation polynomials in Newton's form """ struct InterpPolynomial<:AbstractPolynomial degree::Int xval::NTuple{N, Float64} where N coeff::NTuple{N, Float64} where N end """ evpoly(p::Polynomial, z::Real) Evaluate polynomial `p` at `z` using the Horner's rule """ function evpoly(p::InterpPolynomial, z::Real) ans = p.coeff[p.degree+1] for idx = p.degree:-1:1 ans = ans * (z - p.xval[idx]) + p.coeff[idx] end return ans end
تسمى وظيفة حساب قيمة
evpoly()
الحدود في كلتا الحالتين نفس الشيء -
evpoly()
- لكنها تقبل أنواعًا مختلفة من الوسائط.
بالإضافة إلى وظيفة الحساب ، سيكون من الجيد أن تكتب دالة تنشئ كثير الحدود من البيانات المعروفة.
يوجد طريقتان في جوليا: الإنشاءات الخارجية والبناءات الداخلية. المُنشئ الخارجي هو ببساطة دالة تُرجع كائنًا من النوع المناسب. المُنشئ الداخلي هي وظيفة يتم تقديمها داخل وصف الهيكل وتستبدل المنشئ القياسي. يُنصح باستخدام المُنشئ الداخلي لبناء كثيرات الحدود الداخلية ، منذ ذلك الحين
- هو أكثر ملاءمة للحصول على متعدد الحدود ليس من خلال العقد الاستيفاء ومعاملات ، ولكن من خلال العقد وقيم وظيفة محرف
- يجب أن تكون العقد الاستيفاء متميزة
- يجب أن يتطابق عدد العقد والمعاملات
تضمن كتابة مُنشئ داخلي تضمن اتباع هذه القواعد أن جميع المتغيرات التي تم إنشاؤها من نوع
InterpPolynomial
، على الأقل ، يمكن معالجتها بشكل صحيح من خلال وظيفة
evpoly()
.
نكتب منشئ متعدد الحدود العادية التي تأخذ مجموعة أحادية البعد أو مجموعة من المعاملات كمدخل. يستقبل مُنشئ متعدّد الحدود الاستيفاء عقد الاستيفاء والقيم المطلوبة فيها ويستخدم
طريقة الفروق المقسّمة لحساب المعاملات.
""" Polynomial <: AbstractPolynomial Polynomials written in the canonical form --- Polynomial(v::T) where T<:Union{Vector{<:Real}, NTuple{<:Any, <:Real}}) Construct a `Polynomial` from the list of the coefficients. The coefficients are assumed to go from power 0 in the ascending order. If an empty collection is provided, the constructor returns a zero polynomial. """ struct Polynomial<:AbstractPolynomial degree::Int coeff::NTuple{N, Float64} where N function Polynomial(v::T where T<:Union{Vector{<:Real}, NTuple{<:Any, <:Real}})
إلى جانب الجيل الفعلي من كثير الحدود ، سيكون من الجميل أن تكون قادرًا على إجراء العمليات الحسابية معهم.
نظرًا لأن العوامل الحسابية في جوليا هي وظائف عادية ، يضاف إليها ترميز اللانهاية على شكل سكر نحوي (التعبيرات
a + b
و
+(a, b)
صالحة ومتماثلة تمامًا) ، يتم التحميل الزائد بنفس الطريقة طرق إضافية لوظائفهم.
النقطة الدقيقة الوحيدة هي أن كود المستخدم يتم تشغيله من الوحدة
Main
(مساحة الاسم) ، وأن وظائف المكتبة القياسية موجودة في الوحدة النمطية
Base
، لذلك عند التحميل الزائد ، يجب عليك إما استيراد الوحدة النمطية
Base
أو كتابة الاسم الكامل للوظيفة.
لذلك ، نضيف إضافة كثير الحدود مع عدد:
لإضافة اثنين من كثيرات الحدود العادية ، يكفي إضافة المعاملات ، وعند إضافة متعدد الحدود إلى الآخر ، يمكنك العثور على قيم المجموع في عدة نقاط وإنشاء استيفاء جديد منها.
function Base.:+(p1::Polynomial, p2::Polynomial)
بنفس الطريقة ، يمكنك إضافة عمليات حسابية أخرى على كثير الحدود ، مما يؤدي إلى تمثيلها في الكود في تدوين رياضي طبيعي.
هذا كل شيء الآن. سأحاول الكتابة أكثر عن تنفيذ الطرق العددية الأخرى.
في الإعداد ، تم استخدام المواد التالية:
- وثائق لغة جوليا: docs.julialang.org
- منصة مناقشة لغة جوليا: discourse.julialang.org
- جيه ستوير ، و. مقدمة في التحليل العددي
- جوليا هب: habr.com/en/hub/ جوليا
- اعتقد جوليا: benlauwens.imtqy.com/ThinkJulia.jl/latest/book.html