uk
Feedback
ToCode

ToCode

Відкрити в Telegram

טיפים קצרים למתכנתים מאת ינון פרק

Показати більше
1 420
Підписники
Немає даних24 години
+27 днів
-230 день
Архів дописів
ToCode
1 420
# היום למדתי: סימון פונקציה בתור Deprecated ב TypeScript בעבודה על מערכות לאורך זמן יהיו לנו מצבים שנשנה את דעתנו לגבי ממשק ונרצה לשכנע את המתכנתים האחרים בצוות (או את עצמנו של העתיד) להפסיק להשתמש בפונקציה מסוימת ולעבור להשתמש במקומה בפונקציה אחרת - למשל כי הפונקציה האחרת כוללת יותר יכולות או כתובה בצורה שיותר קל לי לבדוק אותה. עם טייפסקריפט ו VS Code סימון כזה הוא יחסית פשוט: 1. אנחנו מוסיפים את המילה @deprecated בהערת JSDoc מעל הגדרת הפונקציה אותה רוצים לסמן בתור Deprecated. 2. באופן אוטומטי כש VS Code ימצא שהשתמשתי באותה פונקציה הוא יסמן קו באמצע של הקוד שמפעיל את הפונקציה (strike through) וכשאני אעבור על הסימון עם העכבר אני אוכל לראות את כל ההערה, שם בדרך כלל תהיה המלצה במה להשתמש במקום. דוגמה? בטח. נסו להדביק את הקוד הבא בקובץ טייפסקריפט ותראו את הקסם:
/**
 * @deprecated The method should not be used. Use `bar` instead.
 */
function foo(x: number) {
    return x + x;
}

function bar(x: number) {
    return x * 2;
}

const x = foo(10);
const y = bar(20);
ואם אין לכם פרויקט טייפסקריפט זמין אפשר לראות את האפקט גם לייב ב TypeScript Playground בקישור הזה.

ToCode
1 420
# שתי דרכים לבנות מערכת (או: עניין של העדפה) יש כאלה שיעדיפו לבנות מערכת מלמטה למעלה: להתחיל עם מעט מאוד תלויות, ורק כשמזהים צורך או בעיה מוסיפים עוד ספריה חיצונית. אחרים יעדיפו ללכת בכיוון ההפוך: להתחיל עם משהו כמה שיותר "שלם" (כמו ריילס, אנגולר או create-react-app) וככל שמתקדמים ומבינים טוב יותר את המערכת והצרכים אפשר למחוק ממנו את מה שלא צריך. פחות חשוב מה הדרך המועדפת עליכם. יותר חשוב לא להיתקע באמצע הדרך, כלומר לא להישאר עם יותר מדי תלויות וגם לא עם פחות מדי. בסופו של יום "קוד מיותר" הוא בעיה בדיוק כמו "להמציא מחדש את הגלגל".

ToCode
1 420
# טיפ ריסלקט - שימו לב כשאתם מפעילים filter או map בתוך Selector ספריית Reselect עוזרת לנו לבנות פונקציות שמושכות מידע מ Redux Store. ריסלקט מגיעה כחלק מספריית Redux Toolkit ובשביל להשתמש בה בפרויקט אנחנו צריכים רק לייבא את הפונקציה המרכזית שלה createSelector:
import { createSelector } from "@reduxjs/toolkit";
בשביל להבין למה צריך את createSelector נצטרך להיזכר איך עובדת הפונקציה useSelector בה אנחנו משתמשים בתוך קומפוננטות: 1. בתוך קוד קומפוננטה אני קורא ל useSelector כדי "לחבר" בין קוד הקומפוננטה לנתיב מסוים באוביקט ה State ב Redux. 2. הפרמטר שאני מעביר ל useSelector הוא פונקציה בעצמו - פונקציה שמקבלת את כל הסטייט ומחזירה נתיב מסוים, לדוגמה אני יכול למצוא בתוך קוד קומפוננטה את השורה:
const todos = useSelector((state: AppState) => state.todos);
כדי לגשת לכל מערך ה todos שיש בסטייט. 3. כל פעם שמישהו באיזשהו מקום במערכת עושה dispatch, כל פונקציות ה useSelector יתעוררו ויפעילו את הפונקציות שהן קיבלו כפרמטרים. אלה שיחזירו ערך חדש יגרמו לרינדור מחדש של הקומפוננטה. זה אומר שבכל Action שישלח ל Store, הקוד שלי יסתכל על השדה todos וישווה אותו לערך הקודם. 4. לפעמים אני רוצה להפעיל חישובים מורכבים בתוך useSelector. במצב כזה אולי לא משתלם להפעיל את כל החישוב מחדש, במיוחד אם אני יודע במה החישוב תלוי. לדוגמה נתבונן ב Selector הבא:
const todosCount = useSelector((state: AppState) => state.todos.length);
אנחנו יודעים בוודאות שרק אם state.todos השתנה יש סיכוי בכלל שהאורך ישתנה (גם לא בטוח, אבל יש סיכוי). כל עוד state.todos מחזיר את אותו ערך, אין טעם לחשב מחדש את ה length שלו כי הוא בטוח יצא אותו דבר. וזאת בדיוק המטרה של Reselect. 5. הספריה Reselect מאפשרת לנו לתאר את הקשר בין Selector-ים שונים, וכך לחסוך חישובים מיותרים. סלקטור יחושב מחדש רק אם חלק מהתלויות שלו השתנו. בואו נראה עוד דוגמה קצת יותר מורכבת אבל עדיין בעולם של Todos:
import { createSelector } from "@reduxjs/toolkit";
import { AppState } from "./store";

const todos = (state: AppState) => state.todos;

export const todosThatStartWithA = createSelector(todos, (todos) =>
  todos.filter((t) => t.message.startsWith("a"))
);

export const finishedTodosThatStartWithA = createSelector(
  todosThatStartWithA,
  (todos) => todos.filter((t) => t.completed)
);

export const numberOfFinishedTodosThatStartWithA = createSelector(
  finishedTodosThatStartWithA,
  (finishedTodosThatStartWithA) => {
    console.log(`Recalculating the length`);
    return finishedTodosThatStartWithA.length;
  }
);
יש לי כל מיני שאלות שקשורות ל todos, למשל: 1. מי ה todos במערך שההודעה שלהם מתחילה ב a ? 2. מי ה todos במערך שההודעה שלהם מתחילה ב a, והם מסומנים בתור completed ? 3. כמה todos יש במערך שגם ההודעה שלהם מתחילה ב a וגם מסומנים בתור completed ? השימוש ב createSelector מספק דרך נוחה לשלב כמה Selectors, וגם לחשב מחדש כל Selector רק כשהתלויות שלו באמת משתנות. אבל בואו לא נתבלבל, אפילו Reselect אינו קוסם. למרות שהכתיב נותן הרגשה כאילו ה Selectors תלויים אחד בשני, בפועל בגלל ששני ה Selectors שבאמצע משתמשים ב filter (ולכן מייצרים מערך חדש), מצב התלויות הוא שכל ה Selectors תלויים ב state.todos. ברגע שמערך ה todos משתנה מכל סיבה שהיא, כל ה Selectors יחושבו מחדש, כי todosThatStartWithA ו finishedTodosThatStartWithA תמיד מחזירים מערך חדש. אם נריץ את הקוד נגלה שכל שינוי ב todos, גם אם ה todos שהשתנו לא מתחילים ב a, עדיין גרם לחישוב מחדש של ה Selector האחרון. בקיצור גם כשרידאקס נותן לכם מתנות, תמיד צריך לשים לב לתלויות שלנו ולרינדורים כפולים. זה לא תמיד בעיה אבל כן כדאי להיות מודעים למה קורה.

ToCode
1 420
# הצצה לעולם פוסט GPT3 שירות הצ'ט החדש של OpenAI פותח צוהר לעולם עתידני בו (הרבה יותר קרוב ממכוניות מעופפות. כנראה גם הרבה יותר קרוב ממכוניות שנוסעות לבד) בו מחשבים יקראו דרישות של אנשי מוצר ויכתבו בעצמם את רוב הקוד. כבר היום אנחנו יכולים לבקש ממחשב לכתוב תוכניות פשוטות והוא די מבין את המשימה, למשל אני ביקשתי ממנו ב Playground:
Implement a program that reads a file and writes its lines backwards
וקיבלתי את הקוד הבא בפייתון:
import sys

def main():
    if len(sys.argv) != 2:
        print("Usage: python reverse.py <file>")
        sys.exit(1)

    file = sys.argv[1]
    with open(file, 'r') as f:
        lines = f.readlines()
        lines.reverse()
        for line in lines:
            print(line, end='')

if __name__ == "__main__":
    main()
בעוד מספר שנים הם יקראו לבד את כרטיסי הג'ירה שלכם ויפתרו באגים. הם גם יכתבו פיצ'רים חדשים ואולי אפילו מערכות. וכן הם הולכים להיות הרבה יותר זולים מכל מתכנת אנושי שתצליחו למצוא. אז מה, להחליף מקצוע? נדמה לי שהמסקנה היא בדיוק הפוכה: 1. ככל שמחשבים יכתבו חלקים גדולים יותר בקוד שלנו, כך הטעויות שלהם יהיו יותר עדינות. כמו שהיום אנחנו צריכים צוותים של מתכנתים כדי לתחזק אוטומציות, כך אני חושב שנצטרך צוותים של מתכנתים שיבנו ויתחזקו את המודלים שאיתם הבינה המלאכותית כותבת את הקוד. 2. ככל שהבאגים של המחשבים יהיו יותר מעניינים, נצטרך מתכנתים טובים יותר כדי למצוא ולתקן אותם. וכן, המערכות יהיו בסדרי גודל יותר מורכבות כי מחשבים לא ישנים ולא צריכים זמן לחשוב. 3. כמו במנועי חיפוש, כך גם בעבודה עם בינה מלאכותית אנחנו נצטרך לחשוב טוב מה אנחנו מבקשים מהמחשב, והבנה טכנית טובה תהיה המפתח ליצירת בקשות שייצרו תשובות רלוונטיות. 4. עולם הסייבר של היום הוא כלום לעומת מה שמצפה לנו: מצד אחד האקרים שמשתילים קלט זדוני במנגנוני אימון המודלים כדי שהקוד שאותם מודלים יכתבו יכיל דלתות אחוריות עדינות, ומצד שני צורך הולך וגובר במוצרי אבטחה שימצאו ויתקנו את הדלתות האחוריות האלה בצורה אוטומטית. בחזרה לבאגים, בדוגמה אחרת ביקשתי מ GPT3 לייצר לי Unix Pipeline שסוכם את כל המספרים שהוא קיבל מהקלט הסטנדרטי עם השאילתה:
create a unix pipeline to sum all the numbers read from stdin
הוא הציע שני רעיונות, כל אחד עם הבעיות שלו:
# Option 1
cat | tr ' ' '\n' | awk '{ sum += $1 } END { print sum }'

# Option 2
cat | paste -sd+ | bc
בראשון ה cut וה tr מיותרים; השני לא עובד על מק ואיתו כנראה על עוד כמה וריאנטים של יוניקס כי paste אצלנו לא קוראת מ STDIN. ולמרות הכל הסיפור הזה מרשים ומרגש. אם עוד לא קשקשתם עם GPT3 לכו להירשם עכשיו. טירוף מה שהולך שם.

ToCode
1 420
עכשיו שהנתונים מחולקים לקבוצות אפשר לאחד כל קבוצה לשורה ולהשתמש ב egrep כדי למצוא את האות המשותפת לשלושת המילים בשורה:
$ awk '{ print } NR%3 == 0 { print "" }' demo.txt| sed 's/^$/\x0/' | tr '\n' ' ' | xargs -0 -n 1 | egrep -o '([a-zA-Z]).* .*\1.* .*\1.*' | cut -c 1

r
Z
החלפה בעדיפות וסכימה בדיוק כמו בחלק הקודם נותנת לנו:
$ awk '{ print } NR%3 == 0 { print "" }' demo.txt| sed 's/^$/\x0/' | tr '\n' ' ' | xargs -0 -n 1 | egrep -o '([a-zA-Z]).* .*\1.* .*\1.*' | cut -c 1 | xargs -n 1 -I {} grep {} priority| awk '{ print $1 }' | sum

70
## מה הלאה אם הפוסט הזה עשה לכם חשק ללמוד awk יותר לעומק, תשמחו לשמוע שכבר כתבתי עליו מדריך יחסית מפורט בקישור כאן: https://www.tocode.co.il/blog/2022-06-awk. ואם אתם רוצים לראות פיתרונות יותר רציניים לתרגילים האלה, או לשתף פיתרונות שלכם, מוזמנים להצטרף לקבוצה בטלגרם מהקישור בתחילת הפוסט.

ToCode
1 420
# ושוב awk הציל את היום הבטחתי לעצמי שאני לא אעשה את כל ה Advent Of Code-ים השנה, ואני באמת מתכנן להפסיק - אבל עד שזה יקרה אני מקווה להשתמש בחידה של היום כדי להראות עוד כמה טריקים של יוניקס ו awk שאולי יעזרו לכם גם מחוץ לארץ האלפים. ## מה צריך לחשב אם יש לכם סבלנות לחפירות של אריק ווסטל אז אפשר לקרוא את התרגיל המקורי עם סיפורי אלפים באתר המשחק. אבל בקבוצת הטלגרם ים תקצר את זה בצורה מופלאה אז אני פשוט מדביק כאן את הניסוח שלו: לכל אות בטווח a-z יש ערך שנקרא "עדיפות", והוא 1–26 בהתאמה. כנ"ל A-Z, ערכים של 27–52 בהתאמה. חלק 1: חלק כל שורה ל־2. בין החלק הראשון לחלק השני ישנה אות משותפת. מצא את סכום העדיפויות של האותיות המשותפות. חלק 2: עבור כל 3 שורות צמודות בקובץ הקלט, בלי חפיפות (שורות 1–3, 4–6 וכן הלאה), ישנה אות משותפת בין השורות. מצא את סכום העדיפויות של כל האותיות הללו. לדוגמה אם נתון הקלט:
vJrwpWtwJgWrhcsFMMfFFhFp
jqHRNqRjqzjGDLGLrsFMfFZSrLrFZsSL
PmmdzqPrVvPwwTWBwg
wMqvLMZHhHMvwLHjbvcjnnSBnvTQFn
ttgJtRGJQctTZtZT
CrZsJsPPZsGzwwsLwLmpwMDw
אז בחלק הראשון אנחנו צריכים לחלק כל שורה ל-2 בדיוק באמצע, ולמצוא איזה אות מופיעה בשני החצאים, ואז לסכום את האותיות שמצאנו בכל השורות לפי טבלת העדיפויות. בחלק השני צריך לחלק את הטקסט לקבוצות של שלוש שורות ולמצוא את האות המשותפת בין השורות. ## עבודת הכנה אנחנו הולכים להשתמש בכלים הסטנדרטיים של יוניקס בשביל לחשב את שני החלקים, אבל בשביל שהשורות לא יהיו יותר מדי ארוכות אני רוצה להכין שני קיצורים מראש. קודם כל את טבלת העדיפויות של כל אות אפשר (כדאי) ליצור ולשמור לקובץ, כדי שיהיה קל יותר לחפש בה. זה הקוד שיוצר את הקובץ:
$ echo {a..z} {A..Z} | tr ' ' '\n'| cat -n > priority
כל שורה בקובץ מכילה מספר (העדיפות של האות) ואחריו האות שעליה מדברים:
$ head -4 priority
     1  a
     2  b
     3  c
     4  d
פינוק שני הוא ה alias שקורא שורות מ stdin ומחשב את סכום המספרים. השתמשתי בו גם בתרגיל של היום הראשון ובכל מקרה הוא alias שטוב שיהיה על המחשב:
$ alias sum='sed "/./s/^/+/" | tr -d "\n" | xargs echo 0 | bc '
מוכנים? אז יאללה אפשר להוציא את awk. ## חלק 1 האתגר הראשון הוא לחלק כל שורה ל-2. ל awk יש פונקציה בשם substr שבשילוב עם length עושה את הקסם:
$ awk '{ print(substr($0, 0, length/2), substr($0, length/2+1)) }' demo.txt

vJrwpWtwJgWr hcsFMMfFFhFp
jqHRNqRjqzjGDLGL rsFMfFZSrLrFZsSL
PmmdzqPrV vPwwTWBwg
wMqvLMZHhHMvwLH jbvcjnnSBnvTQFn
ttgJtRGJ QctTZtZT
CrZsJsPPZsGz wwsLwLmpwMDw
פשוט הוספתי רווח בדיוק באמצע בין שני החצאים של השורה. אבל עכשיו אנחנו כבר בחצי הדרך לפיתרון. הצעד הבא הוא למצוא את האות שמופיעה בשני החצאים ובשביל זה אפשר להשתמש בביטוי רגולארי:
$ awk '{ print(substr($0, 0, length/2), substr($0, length/2+1)) }' demo.txt | egrep -o '([a-zA-Z]).* .*\1' | cut -c 1

p
L
P
v
t
s
בשביל להחליף כל אות בעדיפות שלה במהירות היה נחמד אם היתה לי טבלת Hash. אפשר להשתמש ב awk כדי לבנות אחת, אבל אני העדפתי לתת למחשב לעבוד קצת יותר ולהשאיר את הקוד יותר קצר אז פשוט שלחתי כל אות ל grep:
$ awk '{ print(substr($0, 0, length/2), substr($0, length/2+1)) }' demo.txt | egrep -o '([a-zA-Z]).* .*\1' | cut -c 1 | xargs -n 1 -I {} grep {} priority

    16  p
    38  L
    42  P
    22  v
    20  t
    19  s
ובסוף שוב awk כדי לקחת רק את העמודה הראשונה ולשלוח אותה ל alias הסכימה שיצרנו בהתחלה:
$ awk '{ print(substr($0, 0, length/2), substr($0, length/2+1)) }' demo.txt | egrep -o '([a-zA-Z]).* .*\1' | cut -c 1 | xargs -n 1 -I {} grep {} priority| awk '{ print $1}' | sum

157
## חלק 2 בחלק השני אנחנו צריכים לקחת כל 3 שורות ולמצוא את האות המשותפת לשלושתן. ומי יותר טוב מ awk בשביל להוסיף שורה רווח אחרי כל שלוש שורות? המשתנה NR מכיל את מספר השורה, ולכן אני יכול לבדוק אם מספר השורה מתחלק ב-3:
$ awk '{ print } NR%3 == 0 { print "" }' demo.txt

vJrwpWtwJgWrhcsFMMfFFhFp
jqHRNqRjqzjGDLGLrsFMfFZSrLrFZsSL
PmmdzqPrVvPwwTWBwg

wMqvLMZHhHMvwLHjbvcjnnSBnvTQFn
ttgJtRGJQctTZtZT
CrZsJsPPZsGzwwsLwLmpwMDw

ToCode
1 420
# איך קוד מתקלקל (דוגמת טייפסקריפט + ריאקט) כתבתי לא מזמן קומפוננטת ריאקט שמקבלת רשימה של פריטים. אני יודע טייפסקריפט אז הוספתי גם הגדרת טיפוסים וזה מה שיצא:
type PinkProps = {
    page?: number;
    itemsPerPage?: number;
    items: Array<{ id: number, text: string }>;
};

function Pink({ page=1, itemsPerPage=25, items }: PinkProps) {
    // component implementation ...
}
אחרי זה היינו צריכים עוד קומפוננטה אבל זו כבר צריכה לקבל רשימת פריטים מסוג אחר. את page ו itemsPerPage משאירים אותו דבר. נו, אני יודע טייפסקריפט חשבתי וכתבתי את הקוד הבא:
type OrangeProps = Pick<PinkProps, "page"|"itemsPerPage"> & {
    items: Array<{ id: number, visible: boolean }>
};

function Orange({ page=1, itemsPerPage=25, items}: OrangeProps) {
    // component implementation ...
}
ומפה לשם היינו צריכים גם פונקציה שמקבלת פריט יחיד מתוך מערך הפריטים ב OrangeProps. שום בעיה חשבתי, אני יודע טייפסקריפט:
function isValid(item: OrangeProps['items'][number]) {
    // validate the item from orange props
}
עכשיו בואו נראה את כל הגדרות הטיפוסים במבט אחד:
type PinkProps = {
    page?: number;
    itemsPerPage?: number;
    items: Array<{ id: number, text: string }>;
};

type OrangeProps = Pick<PinkProps, "page"|"itemsPerPage"> & {
    items: Array<{ id: number, visible: boolean }>
};

function isValid(item: OrangeProps['items'][number]) {
    // validate the item from orange props
}
אם הייתי צריך לכתוב תוכנית מאפס אין סיכוי שהייתי מתחיל עם ההגדרות האלה. קל לראות שאת ההגדרות שקשורות לדפים שווה להוציא לטיפוס אחד, את שני סוגי הפריטים לטיפוס שני ואז לקבל:
type Paginated = {
    page?: number;
    itemsPerPage?: number;
}

type TextItem = {
    id: number;
    text: string;
}

type ToggleItem = {
    id: number;
    visible: boolean;
}

type PinkProps = Paginated & {
    items: Array<TextItem>;
};

type OrangeProps = Paginated & {
    items: Array<ToggleItem>;
}

function isValid(item: ToggleItem) {
    // validate the item from orange props
}

function Pink({ page=1, itemsPerPage=25, items }: PinkProps) {}


function Orange({ page=1, itemsPerPage=25, items}: OrangeProps) {}

ארוך יותר, כן, אבל הרבה יותר ברור וקל להרחבה. לשנות קצת קוד כל פעם רק בשביל להוסיף את הפיצ'ר החדש זו לא גישה מספיק טובה. חייבים להוסיף לה נקודות בקרה בהן אנחנו מארגנים מחדש את הקוד כאילו מתחילים מאפס. אחרת כשנשים לב שדברים מקולקלים זה כבר עלול להיות מאוחר מדי.

ToCode
1 420
היכרות עם שתי השיטות להפוך בלוקים לשורות (awk ו xargs -0) עוזרת לכתוב Pipelines טובים יותר, גם באתגרים אמיתיים מחוץ ל Advent Of Code.

ToCode
1 420
# פיתרון Advent Of Code יום 1 בעזרת יוניקס כמו כל שנה בדצמבר החידות של Advent Of Code התחילו להתפרסם היום. אומנם עדיין לא החלטתי באיזו שפה להשתמש או אפילו אם אני מתכנן לפתור את כולן, אבל החידה הראשונה שהתפרסמה הזכירה לי שלפעמים הכי כיף לקחת כלים פשוטים למשימות פשוטות. בקיצור בואו נראה איך לפתור את Advent Of Code 2022 Day 1 בלי לצאת משורת הפקודה. ## המשימה: חישוב קלוריות באתגר קיבלנו רשימת מספרים שמייצגת כמה קלוריות סוחב האלף הראשון, אחריה שורה רווח ואז עוד רשימת מספרים שמתאימה לקלוריות שסוחב האלף השני, ואז עוד שורת רווח ועוד רשימת מספרים וככה הלאה. בקיצור קלט כזה:
1000
2000
3000

4000

5000
6000

7000
8000
9000

10000
ויש לנו שתי משימות: 1. אם סוכמים את רשימת המספרים בכל קבוצה, מהו הסכום הגבוה ביותר מכל הקבוצות (בקלט הדוגמה זה 24000, סכום הקבוצה הרביעית) 2. מהם שלושת הסכומים הגבוהים ביותר מכל הקבוצות (בקלט הדוגמה זה 45000, הסכום של 24000, 11000 ו-10000). ## איך מחשבים סכומים ביוניקס הפקודה bc היא דרך קלה לחשב סכום של מספרים. כן צריך לעשות קצת מניפולציות על הקלט כדי שיתאים לה, אבל בגדול bc מקבלת תרגיל חשבוני דרך stdin ופותרת אותו. ככה יהיה לנו:
$ echo 2 + 3 | bc
5

$ echo 5 + 7 + 10 | bc
22
בעזרת bc אני יכול לכתוב alias קטן שלוקח רשימת מספרים מ stdin ומדפיס את סכומם:
alias sum='sed "/./s/^/+/" | tr -d "\n" | xargs echo 0 | bc '
ה alias מוסיף סימן פלוס לפני כל מספר (מדלג על שורות ריקות), אחרי זה מוחק את כל הירידות שורה כדי שכל המספרים ייכתבו לשורה אחת, מוסיף 0 בהתחלה ומדפיס את השורה ל stdout בעזרת echo, ומשם ל bc כדי להדפיס את הסכום. ## בחזרה לתרגיל עכשיו שאנחנו יודעים איך עובד חיבור עדיין נשאר לנו לחבר כל קבוצה בנפרד. מאחר שכל קבוצה מופרדת מהקבוצה שאחריה בשורה ריקה, נצטרך להחליף כל "רצף של שורות עד השורה הריקה" בתרגיל חיבור מתאים. במילים אחרות נרצה להפוך את הקלט ל:
0 +1000 +2000 +3000
0 +4000
0 +5000 +6000
0 +7000 +8000 +9000
0 +10000
דרך אחת להפוך קבוצות לשורות היא להפוך כל שורה ריקה לתו Null (כלומר \0), ואז להשתמש ב xargs כדי לקחת את כל הארגומנטים עד תו ה Null. למשל בשביל להדפיס את הטקסט Elf Group לפני כל קבוצה אני יכול להפעיל:
$ cat input.txt| sed 's/^$/\x0/' | xargs -0 -n 1 echo "Elf Group: "
אבל אני לא רוצה להדפיס את התחילית Elf Group אלא לחשב סכום - ובשביל זה צריך להיות קצת יותר יצירתיים: 1. נרצה להוסיף סימן + בתחילת כל שורה לא ריקה בקבוצה. 2. נרצה למחוק את הירידות שורה (כדי לקבל שורה אחת ארוכה שמייצגת תרגיל). 3. נרצה להדפיס 0 בתחילת השורה. ה Pipeline של זה נראה כך:
$ cat input.txt| sed 's/^$/\x0/' | sed '/^[0-9]/s/^/+/' | tr -d '\n' | xargs -0 -n 1 echo 0

0 +1000+2000+3000
0 +4000
0 +5000+6000
0 +7000+8000+9000
0 +10000
ואת זה אפשר לשלוח ל bc כדי להחליף כל שורה בתוצאת התרגיל, כלומר בסכום שלה:
$ cat input.txt| sed 's/^$/\x0/' | sed '/^[0-9]/s/^/+/' | tr -d '\n' | xargs -0 -n 1 echo 0 | bc

6000
4000
11000
24000
10000
השורה עם הסכום הכי גדול? זה פשוט sort ו tail:
$ cat input.txt| sed 's/^$/\x0/' | sed '/^[0-9]/s/^/+/' | tr -d '\n' | xargs -0 -n 1 echo 0 | bc | so
rt -n | tail -1

24000
סכום שלושת השורות הגדולות ביותר? פה אפשר לשלב את ה alias שיצרנו בתחילת הפוסט:
$ cat input.txt| sed 's/^$/\x0/' | sed '/^[0-9]/s/^/+/' | tr -d '\n' | xargs -0 -n 1 echo 0 | bc | sort -n | tail -3 | sum

45000
## כיוון נוסף - awk כלי נוסף שתמיד עוזר לפתור בעיות הוא awk. מאחר ול awk יש כבר תחביר להגדרת משתנים אני יכול פשוט לסכום את כל המספרים עד השורה הריקה, וכשאני מגיע אליה לאפס את הסכום. זאת ההתחלה:
cat input.txt| awk '/^$/ { print sum; sum = 0 } { sum += $1 }'
ושוב בשביל הסכום הגבוה ביותר מספיק להוסיף sort ו tail:
$ cat input.txt| awk '/^$/ { print sum; sum = 0 } { sum += $1 }' | sort -n | tail -1

24000
ובשביל סכום שלושת הגדולים ביותר אני משלב את ה alias מתחילת הפוסט:
$ cat input.txt| awk '/^$/ { print sum; sum = 0 } { sum += $1 }' | sort -n | tail -3 | sum

45000

ToCode
1 420
הי חברים בשעה 10 ניפגש בזום לוובינר טייפסקריפט, אשלח פה לינק כמה דקות לפני למקרה שלא הספקתם להירשם דרך המערכת

ToCode
1 420
# איפה היית עד עכשיו? לכל בעיה בתוכנה יש מספר פיתרונות, חלקם טובים מאחרים. כשאנחנו מסתכלים על מערכת שכבר עובדת ומגלים דרך חדשה טובה יותר, הדבר הראשון שכדאי לעשות הוא Refactoring לקוד כדי לשלב את הדרך החדשה ולוודא שהיא באמת טובה יותר. הדבר השני והחשוב יותר הוא להבין למה לא ראית את הדרך הטובה יותר קודם? איפה כדאי לחפש? ואיזה דברים כדאי לשנות בהתנהלות היום יומית כדי להיחשף ליותר דרכים טובות? מתכנתים טובים מחפשים כל הזמן פיתרונות יעילים יותר, כי פיתרונות טובים יותר נותנים יותר ערך - לא רק שהם ישפרו את הקוד הקיים, הם גם יהפכו את הבעיות הבאות לקלות יותר לפיתרון.

ToCode
1 420
שוב אתם יכולים לשחק עם הקוד באותו קודסנדבוקס בקישור: https://codesandbox.io/s/damp-rain-q71w55?file=/src/App.js כשנלחץ על הכפתור החדש אנחנו רואים שקיבלנו הודעות render רק מהפריטים שבאמת השתנו (שני פריטים, אחד איבד את התוספת לטקסט והשני קיבל תוספת). בתוכניות גדולות יותר, כששמירה על זהות References יכולה לחסוך לכם אפקטים, רנדרים ועוד בלאגנים, טכניקה כזו תוכל לשמור על הקוד שלכם עקבי גם אחרי קבלת עדכונים מהשרת.