Ας υποθέσουμε ότι θέλουμε να αθροίσουμε έναν πίνακα αριθμών. Υπάρχουν πολλοί τρόποι για να πετύχουμε αυτόν τον σκοπό.
Σε πολλές γλώσσες, αυτό το πρόβλημα εκφράζεται ως βρόχος με μια μεταβλητή συσσώρευσης.
Αυτό το πρόβλημα μπορεί να γραφτεί ως αναδρομική συνάρτηση. Σε ψευδοκώδικα, θα μπορούσαμε να έχουμε το εξής.
function Add(X, Sum=0):
if X is empty then
return Sum
else
return Add(rest(X), Sum + first(X))
end
end
Αυτή η μέθοδος διαίρεσης του προβλήματος σε μικρότερα κομμάτια μπορεί επίσης να περιγραφεί ως "αναγωγή προς τη βασική περίπτωση".
Το Reduce είναι ένας τρόπος να συνδυάσουμε όλα τα στοιχεία μιας δομής δεδομένων σε μία μόνο τιμή. Η διαδικασία διατρέχει τη δομή δεδομένων επαναληπτικά, εφαρμόζοντας μια συνάρτηση σε κάθε στοιχείο για να ενημερώσει το συσσωρευμένο αποτέλεσμα.
Στη jq, αυτή η διαδικασία υλοποιείται στο φίλτρο reduce.
Σε άλλες γλώσσες, μπορεί να ονομάζεται "fold", "fold-left", "inject" ή "aggregate".
Η έκφραση reduce της jq μοιάζει με αυτό.
reduce STREAM_EXPRESSION as $var (INITIAL_VALUE; UPDATE_EXPRESSION)
STREAM_EXPRESSION είναι ένα stream στοιχείων, καθένα από τα οποία αποθηκεύεται με τη σειρά του στη μεταβλητή $var.
.[]: $myArray | .[].INITIAL_VALUE είναι η αρχική τιμή του συσσωρευμένου αποτελέσματος (γνωστή ως "συσσωρευτής").UPDATE_EXPRESSION συνδυάζει ("διπλώνει") την τρέχουσα τιμή ($var) στον συσσωρευτή.
. είναι η τιμή του συσσωρευτή.reduce.Ας δούμε ένα παράδειγμα: το άθροισμα των αριθμών ενός πίνακα.
Το φίλτρο add κάνει ακριβώς αυτό, αλλά θα δούμε πώς να το υλοποιήσουμε.
Αν χρησιμοποιήσουμε το [10, 20, 30, 40] ως είσοδο και πάρουμε το μηδέν ως αρχική κατάσταση, να πώς μοιάζει κάθε βήμα.
| # | κατάσταση | στοιχείο | reducer | αποτέλεσμα |
|---|---|---|---|---|
| 1 | 0 | 10 | 0 + 10 | 10 |
| 2 | 10 | 20 | 10 + 20 | 30 |
| 3 | 30 | 30 | 30 + 30 | 60 |
| 4 | 60 | 40 | 60 + 40 | 100 |
Στη σύνταξη της jq, αυτό μοιάζει με τον παρακάτω κώδικα.
0 + 10 | . + 20 | . + 30 | . + 40
Μπορούμε να το εκφράσουμε αυτό με το φίλτρο reduce.
[10, 20, 30, 40] | reduce .[] as $n (0; . + $n) # => 100
Το ενσωματωμένο add στην πραγματικότητα υλοποιείται με το reduce, αλλά χρησιμοποιεί το "null" ως αρχική κατάσταση (σε null μπορεί να προστεθεί οποιοσδήποτε τύπος δεδομένων).
def add: reduce .[] as $x (null; . + $x);
Στην έκφραση αναγωγής, το . είναι ο συσσωρευτής.
Αν η είσοδος είναι κάποιο αντικείμενο που πρέπει να αναφέρεις μέσα στη συνάρτηση αναγωγής, θα πρέπει να το αποθηκεύσεις σε μια μεταβλητή.
{"apple": 10, "banana": 16, "carrot": 4}
| . as $obj
| reduce (keys | .[]) as $key (0; . + $obj[$key]) # => 30
Ο συσσωρευτής μπορεί να είναι οποιουδήποτε τύπου δεδομένων. Για παράδειγμα, μπορεί να θέλεις να αντιστρέψεις έναν πίνακα.
["A", "B", "C", "D"]
| reduce .[] as $elem ([]; [$elem] + .) # => ["D", "C", "B", "A"]
Είσαι δάσκαλος. Στο τέλος της χρονιάς, έχεις υπολογίσει έναν αριθμητικό βαθμό για κάθε μαθητή σου. Τώρα πρέπει να τον μετατρέψεις σε βαθμό με γράμμα και να μετρήσεις πόσοι μαθητές πήραν κάθε βαθμό με γράμμα
Η συνάρτηση letter_grade παίρνει ως είσοδο έναν αριθμητικό βαθμό και επιστρέφει το γράμμα.
Χρησιμοποίησε αυτά τα διαστήματα:
| Γράμμα | Βαθμός |
|---|---|
| A | 90% - 100% |
| B | 80% - 89% |
| C | 70% - 79% |
| D | 60% - 69% |
| F | 0% - 59% |
Παράδειγμα:
75 | letter_grade # => "C"
Η συνάρτηση count_letter_grades παίρνει ένα αντικείμενο που αντιστοιχίζει ονόματα μαθητών στους βαθμούς τους.
Η έξοδος θα είναι ένα αντικείμενο που αντιστοιχίζει κάθε βαθμό με γράμμα στον αριθμό των μαθητών που πήραν αυτόν τον βαθμό.
Παράδειγμα:
{"Joe": 78, "Jane": 93, "Richard": 72} | count_letter_grades
# => {"A": 1, "B": 0, "C": 2, "D": 0, "F": 0}
Υπάρχουν μερικοί διαφορετικοί τρόποι να το λύσεις.
Χρησιμοποίησε το φίλτρο reduce για εξάσκηση.
Γράψου στο Exercism για να μάθεις και να κατακτήσεις jq με 12 έννοιες75 ασκήσεις και πραγματική καθοδήγηση από ανθρώπους, όλα δωρεάν.