المسارات
/
Julia
Julia
/
التمارين
/
اقتراحات المتجر
اقتراحات المتجر

اقتراحات المتجر

تمرين تعلّمي

مقدمة

كل من استخدم Python قد استخدم على الأرجح استيعابات المصفوفات، وهي جزء محوري من صياغة Python منذ إصداراتها المبكرة.

والشيء المريح إلى هذا الحد يجد طريقه تدريجيًا إلى لغات أخرى، ومنها Julia.

الاستيعابات خيار وليست ضرورة لمبرمجي Julia، إذ توجد عادةً بدائل (البثّ، والدوال عالية الرتبة، وما إلى ذلك).

مع ذلك، غالبًا ما يوفّر الاستيعاب طريقة بسيطة ومقروءة وعالية الأداء لبناء مصفوفة. واستخدامه في النهاية مسألة ذوق شخصي، ورأيك في Python مقابل اللغات الوظيفية.

الصياغة في معظمها نسخة مباشرة من Python، لكن مع امتدادات للمصفوفات متعددة الأبعاد.

متغير واحد

أسلوب Pythonي خالص، بما في ذلك جملة if الاختيارية.

julia> [x^2 for x in 1:3]
3-element Vector{Int64}:
 1
 4
 9
julia> [x^2 for x in 1:3 if isodd(x^2)]
2-element Vector{Int64}:
 1
 9

يتوقف شكل المخرجات على تفاصيل الاستيعاب: فهو عادةً يماثل مجموعة الإدخال في الحالات البسيطة، لكنه يُسطَّح إلى Vector بفعل جملة if.

julia> m
2×3 Matrix{Int64}:
 1  2  3
 4  5  6
julia> [x^2 for x in m]
2×3 Matrix{Int64}:
  1   4   9
 16  25  36
julia> [x^2 for x in m if isodd(x^2)] 
3-element Vector{Int64}:
  1
 25
  9

متغيرات متعددة، ومخرجات من نوع Vector

وكما في Python، يمكننا استخدام عدة جمل for بمتغيرات مختلفة، مع مجموعات إدخال متشابهة أو مختلفة.

julia> [x * y for x in 1:3 for y in 4:6]
9-element Vector{Int64}:
  4
  5
  6
  8
 10
 12
 12
 15
 18
julia> [x * y for x in 1:3 for y in 4:6 if isodd(x * y)]
2-element Vector{Int64}:
  5
 15

وهذا مكافئ لحلقات متداخلة. وتكون المخرجات أحادية البعد، حتى عندما يكون الإدخال مصفوفة.

julia> m
2×3 Matrix{Int64}:
 1  2  3
 4  5  6
julia> [x*y for x in m for y in m]
36-element Vector{Int64}:
  1
  4
  2
  5
(truncated output...)

متغيرات متعددة، ومخرجات متعددة الأبعاد

وصف القسم السابق عدة جمل for تفصل بينها مسافات فقط.

وفي هذا القسم توجد جملة for واحدة، والمتغيرات مفصولة بفواصل.

julia> [(x, y) for x in 1:3, y in 4:6]
3×3 Matrix{Tuple{Int64, Int64}}:
 (1, 4)  (1, 5)  (1, 6)
 (2, 4)  (2, 5)  (2, 6)
 (3, 4)  (3, 5)  (3, 6)

كل متغير في الاستيعاب يُنشئ بُعدًا جديدًا في المخرجات، مع عنصر مقابل لكل تركيبة ممكنة من المتغيرات.

في المثال أعلاه، يزداد x نزولًا مع الصفوف، ويزداد y عرضًا مع الأعمدة. ويمكن الوصول إلى أبعاد أعلى، مع التحذيرات المعتادة بشأن سهولة قراءة المخرجات.

تعبيرات المولّد

ركّزت الأقسام السابقة على مخرجات المصفوفات، حيث يوضع الاستيعاب داخل الأقواس المربعة [ ... ].

وعندما نستبدل الأقواس المربعة بالأقواس الهلالية ( ... ) يحدث شيء مختلف: نحصل على generator expression، وهو مكرّر يُقيَّم عند الحاجة ويمكنه إنتاج القيمة التالية عند الطلب.

julia> g = ((x, y) for x in 1:3, y in 4:6)
Base.Generator{Base.Iterators.ProductIterator{Tuple{UnitRange{Int64}, UnitRange{Int64}}}, var"#9#10"}(var"#9#10"(), Base.Iterators.ProductIterator{Tuple{UnitRange{Int64}, UnitRange{Int64}}}((1:3, 4:6)))
# Indexing fails with a generator, the entries don't exist yet
julia> g[1, 2]
ERROR: MethodError: no method matching getindex(::Base.Generator{Base.Iterators.ProductIterator{Tuple{UnitRange{…}, UnitRange{…}}}, var"#9#10"}, ::Int64, ::Int64)
# conversion to array
julia> collect(g)
3×3 Matrix{Tuple{Int64, Int64}}:
 (1, 4)  (1, 5)  (1, 6)
 (2, 4)  (2, 5)  (2, 6)
 (3, 4)  (3, 5)  (3, 6)
# generators are mainly designed for iteration
julia> [i * j for (i, j) in g]
3×3 Matrix{Int64}:
  4   5   6
  8  10  12
 12  15  18

عندما تُستخدم نتيجة الاستيعاب فورًا في مزيد من المعالجة، فقد يكون المولّد موفّرًا للذاكرة، إذ يغني عن تخزين مصفوفة وسيطة كبيرة.

ولا يحتاج المولّد المستخدَم كوسيط دالة إلى أقواس إضافية.

# inefficient
julia> v = [x^2 for x in 1:1e6];
julia> sum(v)
3.333338333335e17
# better - use a generator
julia> sum(x^2 for x in 1:1e6)
3.3333383333312755e17

صياغة المولّد مطابقة لصياغة الاستيعاب، فيما عدا الأقواس المحيطة.

وقد تكون المولّدات ملائمة أيضًا عند إنشاء القواميس.

julia> Dict(x => x^2 for x in 1:5)
Dict{Int64, Int64} with 5 entries:
  5 => 25
  4 => 16
  2 => 4
  3 => 9
  1 => 1

التعليمات

أنت تعمل في متجر بوتيك للأزياء على الإنترنت. وقد أتيت بفكرة ميزة في الموقع تُقترح من خلالها أطقم الملابس على المستخدم. ما يهمنا هنا هو أطقم الملابس المحتملة وأسعارها واستبعاد الخيارات المتضاربة.

Note

قد توجد طرق مختلفة لحل المسائل التالية، لكن يمكن حلها جميعًا باستخدام الاستيعاب.

1. إنشاء قطعة ملابس

نفّذ clothingitem() بحيث تأخذ Tuples اثنين، أحدهما يحتوي على categories والآخر على qualities المقابلة لها. ينبغي أن تُرجع الدالة Dict تكون فيه categories مفاتيح وqualities قيمًا.

julia> categories = ("item_name", "price", "color", "base_color");

julia> qualities = ("Descriptive Name", 99.00, "Ochre Red", "red");

julia> clothingitem(categories, qualities)
Dict{String, Any} with 4 entries:
  "price"      => 99.0
  "item_name"  => "Descriptive Name"
  "base_color" => "red"
  "color"      => "Ochre Red"

ينبغي أن تكون دالتك قادرة على التعامل مع أي عدد من categories.

2. اقتراح التوليفات

نفّذ get_combinations() لتأخذ Tuple من القطع العلوية وTuple من القطع السفلية. ينبغي أن تُرجع الدالة cartesian product للصفوف على هيئة مصفوفة ثنائية الأبعاد (أي Matrix). كل عنصر هو توليفة (top, bottom) على هيئة Tuple. تتغير القطع العلوية على طول الصفوف، وتتغير القطع السفلية على طول الأعمدة.

julia> tops = (
        Dict("item_name" => "Dress shirt"),
        Dict("item_name" => "Casual shirt")
        );

julia> bottoms = (
        Dict("item_name" => "Jeans"),
        Dict("item_name" => "Dress trousers")
       );

julia> get_combinations(tops, bottoms)
2×2 Matrix{Tuple{Dict{String, String}, Dict{String, String}}}:
 (Dict("item_name"=>"Dress shirt"), Dict("item_name"=>"Jeans"))   (Dict("item_name"=>"Dress shirt"), Dict("item_name"=>"Dress trousers"))
 (Dict("item_name"=>"Casual shirt"), Dict("item_name"=>"Jeans"))  (Dict("item_name"=>"Casual shirt"), Dict("item_name"=>"Dress trousers"))

3. جمع أسعار الأطقم

لكل قطعة ملابس حقل price مرتبط بها، لذا قد يكون من المفيد القدرة على مقارنة السعر الكامل للأطقم. نفّذ get_prices() التي تأخذ مصفوفة من توليفات الملابس. ينبغي أن تُرجع الدالة مصفوفة بالشكل نفسه تحتوي على أسعار الأطقم في مواضعها المقابلة.

julia> tops = (
        Dict("item_name" => "Dress shirt", "base_color" => "blue", "price" => 35),
        Dict("item_name" => "Casual shirt", "base_color" => "black", "price" => 20)
       );

julia> bottoms = (
        Dict("item_name" => "Jeans", "base_color" => "blue", "price" => 30),
        Dict("item_name" => "Dress trousers", "base_color" => "black", "price" => 75)
       );

julia> combomatrix = get_combinations(tops, bottoms);

julia> get_prices(combomatrix)
2×2 Matrix{Int}::
 65  110
 50   95

julia> filteredmatrix = filter_clashing(combomatrix)
1-element Vector{Tuple{Dict{String, Any}, Dict{String, Any}}}:
 (Dict("price" => 20, "item_name" => "Casual shirt", "base_color" => "black"), Dict("price" => 30, "item_name" => "Jeans", "base_color" => "blue"))

julia> get_prices(filteredmatrix)
1-element Vector{Int}:
 50

4. استبعاد الأطقم المتضاربة

لكل قطعة ملابس حقل base_color. استخدم هذا الحقل لإزالة كل التوليفات التي يكون فيها اللون الأساسي للقطعة العلوية مطابقًا للون الأساسي للقطعة السفلية. نفّذ filter_clashing() لتأخذ مصفوفة من توليفات الملابس. أرجِع مصفوفة أحادية البعد (أي Vector) تحتوي على التوليفات المطابقة.

julia> tops = (
        Dict("item_name" => "Dress shirt", "base_color" => "blue", "price" => 35),
        Dict("item_name" => "Casual shirt", "base_color" => "black", "price" => 20)
       );

julia> bottoms = (
        Dict("item_name" => "Jeans", "base_color" => "blue", "price" => 30),
        Dict("item_name" => "Dress trousers", "base_color" => "black", "price" => 75)
       );

julia> combos = get_combinations(tops, bottoms);

julia> filter_clashing(combos)
1-element Vector{Tuple{Dict{String, Any}, Dict{String, Any}}}:
 (Dict("price" => 20, "item_name" => "Casual shirt", "base_color" => "black"), Dict("price" => 30, "item_name" => "Jeans", "base_color" => "blue"))
تعديل عبر GitHub يفتح الرابط في نافذة أو علامة تبويب جديدة
Julia Exercism

مستعد لبدء اقتراحات المتجر؟

سجّل في Exercism لتتعلّم وتتقن Julia عبر 35 مفهومًا128 تمرينًا، وإرشاد بشري حقيقي، وكل ذلك مجانًا.