ToCode
رفتن به کانال در Telegram
1 420
مشترکین
اطلاعاتی وجود ندارد24 ساعت
اطلاعاتی وجود ندارد7 روز
-530 روز
آرشیو پست ها
1 420
┌ ⚠️ Deno requests env access to "LC_MESSAGES".
├ Run again with --allow-env to bypass this prompt.
└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all env permissions) >
✅ Granted read access to <CWD>.
✅ Granted env access to "_".
✅ Granted read access to <main_module>.
✅ Granted env access to "SHELL".
✅ Granted read access to "/Users/ynonp/Library/Caches/deno/npm/registry.npmjs.org/cowsay/1.6.0/package.json".
✅ Granted env access to "LC_ALL".
✅ Granted env access to "LC_MESSAGES".
┌ ⚠️ Deno requests env access to "LANG".
├ Run again with --allow-env to bypass this prompt.
└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all env permissions) >
✅ Granted read access to <CWD>.
✅ Granted env access to "_".
✅ Granted read access to <main_module>.
✅ Granted env access to "SHELL".
✅ Granted read access to "/Users/ynonp/Library/Caches/deno/npm/registry.npmjs.org/cowsay/1.6.0/package.json".
✅ Granted env access to "LC_ALL".
✅ Granted env access to "LC_MESSAGES".
✅ Granted env access to "LANG".
┌ ⚠️ Deno requests env access to "LANGUAGE".
├ Run again with --allow-env to bypass this prompt.
└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all env permissions) >
✅ Granted read access to <CWD>.
✅ Granted env access to "_".
✅ Granted read access to <main_module>.
✅ Granted env access to "SHELL".
✅ Granted read access to "/Users/ynonp/Library/Caches/deno/npm/registry.npmjs.org/cowsay/1.6.0/package.json".
✅ Granted env access to "LC_ALL".
✅ Granted env access to "LC_MESSAGES".
✅ Granted env access to "LANG".
✅ Granted env access to "LANGUAGE".
┌ ⚠️ Deno requests read access to "/Users/ynonp/Library/Caches/deno/npm/registry.npmjs.org/cowsay/1.6.0/cows/default.cow".
├ Requested by \Deno.readFileSync()\ API.
├ Run again with --allow-read to bypass this prompt.
└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all read permissions) >
✅ Granted read access to <CWD>.
✅ Granted env access to "_".
✅ Granted read access to <main_module>.
✅ Granted env access to "SHELL".
✅ Granted read access to "/Users/ynonp/Library/Caches/deno/npm/registry.npmjs.org/cowsay/1.6.0/package.json".
✅ Granted env access to "LC_ALL".
✅ Granted env access to "LC_MESSAGES".
✅ Granted env access to "LANG".
✅ Granted env access to "LANGUAGE".
✅ Granted read access to "/Users/ynonp/Library/Caches/deno/npm/registry.npmjs.org/cowsay/1.6.0/cows/default.cow".
_____________
< Hello world >
-------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
אז נכון לאף אחד אין כח לאשר כל הרשאה שאפליקציה צריכה, אבל עם האינטרנט של היום אי אפשר להיות זהירים מדי.1 420
אנחנו ב 2024, ו node.js עדיין מתנהג כאילו זה 2018
נוד הוא כבר מזמן לא סביבת הריצה היחידה בצד שרת להפעלת קוד JavaScript, אבל גם גירסאות עדכניות שלו ממשיכות להציג בעיות שמזמן היו צריכות להיפתר. הנה כמה דוגמאות מהסיבוב האחרון שלי איתו.
תוכניות של קובץ אחד
אנחנו ב 2024 ועדיין אין פיתרון ליצירת סקריפט בקובץ אחד שתלוי בתוכניות אחרות. ב deno אני יכול לכתוב:
import cowsay from 'npm:cowsay@1.6.0';
console.log(cowsay.say({
text : "I'm a moooodule",
e : "oO",
T : "U "
}));
בקובץ אחד ואז להריץ אותו והכל פשוט עובד. ב node צריך להוסיף לתיקיה קובץ package.json ולהכריח את כולם להריץ npm install.
טייפסקריפט
אנחנו ב 2024 ועדיין אני צריך להפעיל tsc בתיקיה לפי שיכול להריץ קבצי TypeScript (כי node מריץ רק JavaScript), אבל אפילו זה לא מספיק כי אם לא תדייק בקבצי ה package.json וה tsconfig.json דברים פשוטים לא יעבדו. שוב להשוואה זה עובד לי ב deno בתור קובץ יחיד בתיקיה:
const res = await fetch('http://api.open-notify.org/astros.json');
const data = await res.json();
console.log(\There are ${data.number} astronauts currently in space\);
אבל ב node אני צריך את שני קבצי ההגדרות ו ts-node בכלל לא מוכן להריץ כזה קובץ גם כש tsc כן מצליח לקמפל אותו.
הפעלת קוד מהאינטרנט בלי לשאול
אני די בטוח שפעם הסכמנו שהקונספט הזה שגוי, אבל איכשהו npm עדיין מריץ המון קוד מהאינטרנט בלי לשאול בכל התקנה של ספריה, או בכל הפעלה של npx.
כך עם npx אני כותב פשוט:
$ npx cowsay hello world
_____________
< hello world >
-------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
וכן הסקריפט cowsay מגיע מהרשת ויכול לעשות מה שהוא רוצה על המחשב שלי.
ב deno אותו משחק נראה ככה:
$ deno run npm:cowsay Hello world
אבל הפלט שונה - במקום להריץ את הסקריפט יש קודם כל רצף של שאלות:
┌ ⚠️ Deno requests read access to <CWD>.
├ Requested by \Deno.cwd()\ API.
├ Run again with --allow-read to bypass this prompt.
└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all read permissions) >
✅ Granted read access to <CWD>.
┌ ⚠️ Deno requests env access to "_".
├ Run again with --allow-env to bypass this prompt.
└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all env permissions) >
✅ Granted env access to "_".
┌ ⚠️ Deno requests read access to <main_module>.
├ Requested by \Deno.mainModule\ API.
├ Run again with --allow-read to bypass this prompt.
└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all read permissions) >
✅ Granted read access to <CWD>.
✅ Granted env access to "_".
✅ Granted read access to <main_module>.
┌ ⚠️ Deno requests env access to "SHELL".
├ Run again with --allow-env to bypass this prompt.
└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all env permissions) >
✅ Granted read access to <CWD>.
✅ Granted env access to "_".
✅ Granted read access to <main_module>.
✅ Granted env access to "SHELL".
┌ ⚠️ Deno requests read access to "/Users/ynonp/Library/Caches/deno/npm/registry.npmjs.org/cowsay/1.6.0/package.json".
├ Requested by \Deno.statSync()\ API.
├ Run again with --allow-read to bypass this prompt.
└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all read permissions) >
✅ Granted read access to <CWD>.
✅ Granted env access to "_".
✅ Granted read access to <main_module>.
✅ Granted env access to "SHELL".
✅ Granted read access to "/Users/ynonp/Library/Caches/deno/npm/registry.npmjs.org/cowsay/1.6.0/package.json".
┌ ⚠️ Deno requests env access to "LC_ALL".
├ Run again with --allow-env to bypass this prompt.
└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all env permissions) >
✅ Granted read access to <CWD>.
✅ Granted env access to "_".
✅ Granted read access to <main_module>.
✅ Granted env access to "SHELL".
✅ Granted read access to "/Users/ynonp/Library/Caches/deno/npm/registry.npmjs.org/cowsay/1.6.0/package.json".
✅ Granted env access to "LC_ALL".1 420
שני תרגילי עוקץ לכבוד פורים
אנחנו ב 2024, וכדאי לזכור שתרגילי עוקץ לא מתו - וזה כולל בתעשיית ההייטק כשאנשים כביכול מודעים לכל הסכנות. הנה שני סיפורים שנתקלתי בהם בתקופה האחרונה ורציתי לשתף כדי להעלות קצת את רמת העירנות של כולנו.
קורסי סקאלה מזויפים
דמיינו שיש לכם ניסיון די טוב בסקאלה אבל בגלל שהשפה כבר פחות פופולרית ממה שהיתה אתם לא ממש מצליחים למצוא עבודה. אחרי כמה חודשים של שליחת קורות חיים ללא מענה יוצרת אתכם קשר מגייסת שבדיוק מנסה לאייש תפקיד סקאלה בחברה גלובאלית גדולה.
היא קובעת לכם ראיון סינון ואז ראיון טכני ואחרי שאתם כבר מריחים את החוזה מגיע האימייל: תשמע אתה בחור נחמד ורואים שיש לך ניסיון בסקאלה אבל אנחנו מחפשים מישהו עם קצת יותר הבנה גם של התיאוריה. נוכל לקבל אותך אחרי שתסיים קורס מסודר בסקאלה ותוציא תעודת הסמכה. ואז הם מצרפים לינק לקורס ולהסמכה.
פה חשדתי.
מי שמכיר את האקוסיסטם יוכל לזהות בקלות שההסמכה פיקטיבית. אלה שנפלו בעוקץ מדווחים שאחרי שסיימו את ההסמכה (200$ בשביל לגשת למבחן) החברה המגייסת נעלמה, ומבחן ההסמכה עצמו היה ברמה מאוד בסיסית.
כל הפרטים על העוקץ באתר הרשמי של סקאלה כאן:
https://www.scala-lang.org/blog/2024/03/01/fake-scala-courses.html
רומנטיקה מזויפת באפריקה
חבר קיבל הודעה ברשת חברתית ללומדי שפות ממישהי שרוצה "ללמוד את השפה שלו". אחרי כמה שיחות התקשורת עברה לפסים רומנטיים. כל פעם שהם קבעו להיפגש משהו קרה והיא היתה צריכה לבטל ברגע האחרון. על הדרך בשיחות הוא הבין שהיא בסוג של מצוקה כלכלית. פתאום היא נעלמה לכמה ימים וכשהופיעה שוב ברשת היא צלצלה כביכול מטוגו שבאפריקה. "אתה לא תאמין" היא אמרה, "אבל קיבלתי ירושה גדולה ונסעתי לטוגו כדי לחתום אצל עורכי הדין".
פה חשדתי.
אחרי כמה ימים היא כבר התחילה לבקש כסף כדי לשלם לאותם עורכי דין. החבר הבין את העוקץ וניתק קשר.
זה עדיין עובד לכם?
כשאני שומע על סיפורי עוקץ כאלה וגם מקבל אימיילים מוזרים, שיחות ממספרים מוזרים או כל תקשורת לא צפויה אחרת תמיד אני רוצה לשאול "זה עדיין עובד לכם?", איך זה יכול להיות שב 2024 אנשים עדיין מבצעים תרגילי עוקץ דיגיטליים? איך לא נהיינו מספיק חשדנים כדי לחתוך כשדברים כאלה רק מתחילים?
אז מסתבר שזה עדיין עובד. אנחנו לא מספיק חשדנים. האינטרנט יכול להיות כלי נפלא אבל תקשורת מהירה עם כל העולם מזמינה גם רמאים.
1 420
כשאני לא יודע לכתוב Type Hint בפייתון
מנגנון Type Hints בפייתון הוא אחד המנגנונים הכי חשובים לבניית קוד קריא וממשק יציב. אבל באותה נשימה צריך גם להגיד שהוא "הולבש" על השפה באיחור, ויהיו מצבים שלמרות כל הרצון הטוב ניתקע ולא נדע איך לכתוב את ה Type Hint הטוב ביותר.
במצב כזה יש שתי אפשרויות - או לוותר על ה Type Hint, או לשנות את הקוד כדי שנוכל להסתפק ב Type Hint פחות מתוחכם. ההתלבטות קשה.
דוגמה? בטח. נדמיין פונקציה שמקבלת רשימה ומחזירה ערך אקראי ממנה לפי משקל. כל אחד מהפריטים ברשימה מחזיק מאפיין בשם weight שהוא המשקל של אותו פריט. אני יודע יש כבר אחת בפייתון אבל דמיינו שלא היתה. הנה הקוד:
def random_weighted_item(items):
items = sorted(items, key=lambda x: x.weight)
min_weight = min(i.weight for i in items)
max_weight = max(i.weight for i in items)
normalized_weights = [(i.weight - min_weight) / (max_weight - min_weight) for i in items]
cumulative_sum = list(accumulate(normalized_weights))
randomized_weight = random.random() * cumulative_sum[-1]
index = next(i for i, e in enumerate(cumulative_sum) if e > randomized_weight)
return items[index]
אפשר להוסיף לזה בקלות Type Hints בעזרת Type Var וזה יראה כך:
class HasWeight(Protocol):
weight: int
T = TypeVar("T", bound=HasWeight)
def random_weighted_item(items: list[T]) -> T:
...
אבל עכשיו נדמיין שאנחנו צריכים לתמוך בפריטים שהמשקל שלהם מחושב בכל מיני דרכים. אז אנחנו מוסיפים עוד פרמטר שהוא פונקציה המקבלת פריט ומחזירה את המשקל והכל עדיין עובד:
def random_weighted_item(items: list[T], weight: Callable[[T], int]) -> T:
...
אבל אז אנחנו רוצים לתמוך גם בהפעלה הקודמת (בלי פונקציית המשקל) ואת זה פייתון כבר לא אוהב:
def random_weighted_item(items: list[T], weight: Callable[[T], int] = lambda i: i.weight) -> T:
הודעת השגיאה היא:
weighted_random_demo.py:44: error: "T" has no attribute "weight" [attr-defined]
Found 1 error in 1 file (checked 1 source file)
עכשיו יכול להיות שאפשר לסדר את זה עם כמה חתימות של פונקציה ב Type Hint וזה רק אני שלא הצלחתי, ויכול להיות שבאמת לפייתון אין פיתרון למצב כזה. זה לא חשוב. בסיטואציה כזאת יש לנו שתי אפשרויות-
1. לבחור חתימה שעובדת יותר טוב עם ה Type Hints (למשל כמו choices שמקבלת רשימה של פריטים ורשימה של משקלים)
2. לוותר על ה Type Hints ולהתעקש על החתימה שבחרנו.
ההתלבטות קשה. בדוגמה כאן עדיף לשנות את החתימה כי החתימה של choices באמת יותר ברורה. במקרה הכללי יהיו גם מצבים שנעדיף לשים בצד את ה Type Hint ולחזור להוסיף אותו בהמשך, או כשאנחנו נדע יותר על Type Hints או כשהמנגנון ישתפר בגירסה חדשה יותר של פייתון.
נ.ב. אם אתם מכירים פיתרון של Type Hints לחתימה מהדוגמה אשמח לשמוע בתגובות או בטלגרם.1 420
import scala.util.chaining._
import scala.collection.immutable.ListMap
object aoc2023day15 {
val demoInput: String = "rn=1,cm-,qp=3,cm=2,qp-,pc=4,ot=9,ab=5,pc-,pc=6,ot=7"
def runningHash(input: String): Int =
val currentValue = 0
input.map(_.toInt).foldLeft(currentValue) {(currentValue, asciiCode) =>
((currentValue + asciiCode) * 17) % 256
}
def focusingPower(boxNumber: Int, index: Int, value: Int): Long =
(boxNumber + 1) * (index + 1) * value
@main
def day15part1(): Unit =
Source
.fromResource("day15.txt")
.getLines()
.next()
.split(',')
.map(runningHash)
.sum
.pipe(println)
@main
def day15part2(): Unit =
Source
.fromResource("day15.txt")
.getLines()
.next()
.split(',')
.foldLeft(Map[Int, ListMap[String, Int]]()) { (boxes, instruction) =>
instruction.split("[-=]") match
case Array(label, value) =>
val boxNumber = runningHash(label)
boxes.updatedWith(boxNumber) {
case Some(lenses) => Some(lenses.updated(label, value.toInt))
case None => Some(ListMap(label -> value.toInt))
}
case Array(label) =>
val boxNumber = runningHash(label)
boxes.updatedWith(boxNumber) {
case Some(lenses) => Some(lenses.removed(label))
case _ => None
}
}
.map { (k, v) => (k, v.zipWithIndex.map {
case ((label, value), index) => focusingPower(k, index, value)
}) }
.values
.map(_.sum)
.sum
.pipe(println)
}1 420
פיתרון Advent Of Code יום 15 בסקאלה - איזה כיף שהמציאו את ListMap
התרגיל של יום 15 ב Advent Of Code היה בגדול ממש קל, במיוחד בהשוואה לימים הקודמים עם המטריצות. בפוסט הפעם לא אכתוב את הסיפור המלא (בעיקר כי הוא עמוס בחישובים לא מעניינים) ונתמקד בחלק היחיד שכן היה מעניין ביום הזה - סידור העדשות בקופסאות.
האתגר
נדמיין 256 קופסאות, בכל קופסה אפשר לשים עדשות ולכל עדשה יש תווית (טקסט) וערך (מספר). אנחנו מקבלים בתור דף הוראות רשימה של תוויות וערכים וצריכים לשים את העדשות בתוך הקופסאות לפי הכלל הבא:
1. אם התווית כבר נמצאת בקופסה, יש להחליף את הערך.
2. אם התווית לא בקופסה יש להוסיף אותה בתור העדשה האחרונה של הקופסה.
3. אם לא עבר ערך יש להוציא את העדשה עם התווית מהקופסה.
האתגר הוא לשמור על סדר ההכנסה בתוך כל קופסה כי בסוף השאלה היא לחשב איזשהו סכום שמערב את האינדקס של כל עדשה בקופסה.
קלט לדוגמה נראה כך:
rn=1,cm-,qp=3,cm=2,qp-,pc=4,ot=9,ab=5,pc-,pc=6,ot=7
ויש גם פונקציה שמקבלת תווית ומחזירה את מספר הקופסה שלתוכה צריך להכניס את העדשה שזו התווית שלה. הפיתרון עבור קלט הדוגמה הוא:
After "rn=1":
Box 0: [rn 1]
After "cm-":
Box 0: [rn 1]
After "qp=3":
Box 0: [rn 1]
Box 1: [qp 3]
After "cm=2":
Box 0: [rn 1] [cm 2]
Box 1: [qp 3]
After "qp-":
Box 0: [rn 1] [cm 2]
After "pc=4":
Box 0: [rn 1] [cm 2]
Box 3: [pc 4]
After "ot=9":
Box 0: [rn 1] [cm 2]
Box 3: [pc 4] [ot 9]
After "ab=5":
Box 0: [rn 1] [cm 2]
Box 3: [pc 4] [ot 9] [ab 5]
After "pc-":
Box 0: [rn 1] [cm 2]
Box 3: [ot 9] [ab 5]
After "pc=6":
Box 0: [rn 1] [cm 2]
Box 3: [ot 9] [ab 5] [pc 6]
After "ot=7":
Box 0: [rn 1] [cm 2]
Box 3: [ot 7] [ab 5] [pc 6]
פיתרון כללי
מבנה הנתונים המרכזי שעזר לי לפתור את התרגיל הזה נקרא בסקאלה ListMap (הוא קיים גם בפייתון ושם נקרא OrderedDict. זה מבנה נתונים שהוא שילוב בין מילון לרשימה - מכניסים אליו דברים לפי מפתחות אבל הוא שומר על סדר ההכנסה שלהם. בסקאלה שימוש לדוגמה בו נראה כך:
scala> ListMap("foo" -> 10, "bar" -> 20, "buz" -> 30)
val res2: scala.collection.immutable.ListMap[String, Int] = ListMap(foo -> 10, bar -> 20, buz -> 30)
scala> ListMap("foo" -> 10, "bar" -> 20, "buz" -> 30).updated("bar", 40)
val res3: scala.collection.immutable.ListMap[String, Int] = ListMap(foo -> 10, bar -> 40, buz -> 30)
scala> ListMap("foo" -> 10, "bar" -> 20, "buz" -> 30).updated("foo", 30)
val res4: scala.collection.immutable.ListMap[String, Int] = ListMap(foo -> 30, bar -> 20, buz -> 30)
scala> ListMap("foo" -> 10, "bar" -> 20, "buz" -> 30).updated("hiz", 50)
val res5: scala.collection.immutable.ListMap[String, Int] = ListMap(foo -> 10, bar -> 20, buz -> 30, hiz -> 50)
כלומר כל שינוי במילון ישמור תמיד על סדר המפתחות, ויש גם פונקציות לאיטרציה על המילון שתמיד יעבדו לפי סדר ההכנסה. אם ערך במפתח מסוים עודכן הוא עדיין ישמור על המיקום שלו לפי סדר ההכנסה המקורי.
וזה מצוין כי עכשיו אפשר לתרגם את משימת העדשות לפעולות על מילון-רשימה:
1. בכל קופסה נשים ListMap.
2. כל פקודה על עדשה (הכנסה, שינוי או הוצאה) תתורגם לפעולה על אותו ListMap.
3. בסוף יהיו לנו את כל המפתחות לפי סדר ההכנסה.
קוד? זה החלק הרלוונטי מתוך התוכנית:
.foldLeft(Map[Int, ListMap[String, Int]]()) { (boxes, instruction) =>
instruction.split("[-=]") match
case Array(label, value) =>
val boxNumber = runningHash(label)
boxes.updatedWith(boxNumber) {
case Some(lenses) => Some(lenses.updated(label, value.toInt))
case None => Some(ListMap(label -> value.toInt))
}
case Array(label) =>
val boxNumber = runningHash(label)
boxes.updatedWith(boxNumber) {
case Some(lenses) => Some(lenses.removed(label))
case _ => None
}
}
לוקחים הוראה, מפצלים אותה לפי הפסיק או המינוס, מגלים את מספר הקופסה שמתאים לה ומעדכנים את הקופסה המתאימה.
לסקרנים קוד התוכנית המלא עם כל החישובים הפחות מעניינים מהתרגיל הוא:
import scala.io.Source1 420
היום למדתי: scope ב CSS
אני אוהב איך ש CSS מתפתח ומצליח לפתור את הבעיות תמיד שלוש שנים אחרי הזמן. הסיפור היום הוא על פיצ'ר מ CSS Selectors 4 שנקרא scope שממש הייתי צריך כל החיים אבל בכל מקרה אנחנו לא פה בשביל להתלונן וטוב מאוחר מלעולם לא.
מה ש scope עושה זה נותן לנו להגדיר כללי CSS שמשפיעים רק על אזור מסוים. כלומר אם יש לנו את ה HTML הזה (מתוך הדוגמה ב MDN):
<div class="light-scheme">
<p>
MDN contains lots of information about
<a href="/en-US/docs/Web/HTML">HTML</a>,
<a href="/en-US/docs/Web/CSS">CSS</a>, and
<a href="/en-US/docs/Web/JavaScript">JavaScript</a>.
</p>
</div>
<div class="dark-scheme">
<p>
MDN contains lots of information about
<a href="/en-US/docs/Web/HTML">HTML</a>,
<a href="/en-US/docs/Web/CSS">CSS</a>, and
<a href="/en-US/docs/Web/JavaScript">JavaScript</a>.
</p>
</div>
ואני רוצה לצבוע כל אחד מהדיבים והלינקים שאיתו בצבעים שונים תמיד יכלתי לכתוב CSS בסגנון הזה:
.light-scheme {
background-color: plum;
}
.light-scheme a {
color: darkmagenta;
}
.dark-scheme {
background-color: darkmagenta;
color: antiquewhite;
}
.dark-scheme a {
color: plum;
}
די הרבה זמן אני כבר יכול להשתמש ב CSS כזה:
.light-scheme {
background-color: plum;
a {
color: darkmagenta;
}
}
.dark-scheme {
background-color: darkmagenta;
color: antiquewhite;
a {
color: plum;
}
}
ולאחרונה אני יכול להשתמש גם בכתיב הזה כדי להגיע לאותה תוצאה:
@scope (.light-scheme) {
:scope {
background-color: plum;
}
a {
color: darkmagenta;
}
}
@scope (.dark-scheme) {
:scope {
background-color: darkmagenta;
color: antiquewhite;
}
a {
color: plum;
}
}
ההבדל בין שני האחרונים קשור למשמעות הסמנטית שלהם. האמצעי בעל אותה משמעות כמו הראשון וה Specificity של ההורה נלקח בחשבון כשמחשבים את הערך (צבע במקרה שלנו). ב scope ההורה הוא לא חלק משורת ה Selector ולכן לא נלקח בחשבון בפיתרון קונפליקטים. דוגמה? בטח. ה CSS הזה:
.light-scheme a {
color: yellow;
}
@scope (.light-scheme) {
:scope {
background-color: plum;
}
a {
color: darkmagenta;
}
}
נותן לאלמנטי ה a צבע צהוב, למרות שבתוך הסקופ מוגדר ערך אחר. הערך העליון הוא יותר ספציפי ולכן מנצח. לעומתו ה CSS הזה:
div {
padding: 10px;
}
.light-scheme a {
color: yellow;
}
.light-scheme {
background-color: plum;
a {
color: darkmagenta;
}
}
ייתן ללינקים שבתוך ה light-scheme את הצבע darkmagenta בגלל שהסלקטור שלו יותר ספציפי.1 420
טרייד אוף לא הגיוני
וויז החליטו שאני צריך לנסוע מרחק כפול (16 ק"מ במקום 8) כדי לחסוך 3 דקות בנסיעה. האלגוריתם לא חשב שאולי הוא טועה וגם בדרך הארוכה עשוי להיות פקק, ובפועל אני רק מגדיל את הסיכון.
במקום אחר, lcl.host החליטו שיהיה להם יותר קל אם רק קלאיינטים בגירסה הכי חדשה יוכלו להתחבר. הם לא חשבו שאולי הם לא יוכלו להוציא גירסה חדשה באותו זמן לכל דרכי ההפצה וכך אנשים נתקעו בלי יכולת להשתמש בתוכנה (יש גירסה חדשה זמינה אבל עדיין לא בשביל המחשב שלך).
בעולם ה JavaScript אנחנו רגילים לראות התפוצצות של גודל הדפים בגלל שכולם משתמשים בפריימוורקים ומוסיפים תלויות כאילו אין מחר. בסוף כל הקוד הזה צריך לרוץ אצל מישהו על המכונה וגורם לאיטיות בטעינה ובזמן הריצה.
״כמה זה עולה?״ זאת תמיד שאלה חשובה כי רק דרכה אפשר להבין מה הטרייד אוף שעל הפרק ולחשוב אם הוא מתאים לאתגר שלנו.
1 420
מחפש לומדי שפות אמיצים לפרויקט חדש
הי חברים,
חלק מכם יודעים כבר שחוץ מלכתוב על שפות תכנות אני גם אוהב ללמוד שפות מדוברות (כן רגילות של אנשים), ואחד הקשיים בלימוד שפה הוא שיפור אוצר המילים. אין בעיה למצוא מילים חדשות, הן שם בכל ספר, סידרה או פוסט שקוראים בשפה החדשה. גם אין בעיה למצוא את התרגום שלהן והיום בלחיצת כפתור מתוך הדפדפן אפשר לראות תרגום של כל מילה ואפילו של עמודים שלמים. אבל כשאנחנו מגיעים לכתוב או לדבר אנחנו מגלים שאותן מילים יפות שמצאנו ברשת פשוט נעלמו לנו מהמוח.
וזה גם הגיוני. המוח שלנו לא אוהב לבזבז אנרגיה בלשמור מידע שהוא לא צריך, ולכן אם אנחנו רואים מילה שוב ושוב בהקשרים שונים לא תהיה לנו בעיה להשתמש בה, אבל אם נתקלנו במילה פעם אחת באיזה פוסט אז היא לא נחשבת מספיק חשובה וייקח למוח יותר זמן להיזכר בה.
מה עושים? את הפיתרון לא אני המצאתי והוא נקרא Spaced Repetition. בגדול אנחנו גורמים למוח לחשוב שמילה היא חשובה כדי שהמוח ייצור עבורה את האינדקס המתאים ויהיה קל להיזכר בה כשנצטרך. הבעיה שלמרות שניסיתי המון אפליקציות מהסוג הזה אף אחת לא עבדה כמו שצריך ובסוף נטשתי את כולן. בשביל לנסות להוכיח שהבעיה לא בי אלא באפליקציות התחלתי לכתוב מערכת משלי לשינון מילים ובינתיים היא עובדת לי לא רע, וכאן אנחנו מגיעים לקריאה לפעולה של הפוסט הזה - אני מחפש להכניס עוד משתמשים למערכת כדי לקבל יותר פידבק ולעזור לי לראות את הדברים שעדיין צריכים שיפור.
לכן אם אתם-
1. דוברי עברית שרוצים לשפר את האנגלית שלכם, או דוברי אנגלית שרוצים לשפר את הספרדית או הצרפתית שלכם.
2. לומדים באופן פעיל את השפה (קוראים, רואים סרטים, מדברים) ונתקלים באופן שוטף במילים חדשות שהייתם רוצים לזכור.
3. שמחים לנסות מוצרים חדשים לפני שהם מוכנים ולשתף פידבק מועיל כדי לעזור לשפר.
בבקשה השאירו לי הודעה כאן באתר או למייל ynon שטרודל tocode.co.il ואכניס אתכם לקבוצת הניסוי.
1 420
טכניקה וקצב
כשהטכניקה טובה אין בעיה לעבוד לאט. הקצב יגיע בקצב שלו. אבל כשהקצב מהיר מאוד קשה לשים לב לפרטים ולשפר את הטכניקה.
בקוד מאוד קל לראות את זה - מתכנתים רצים כדי להספיק את הדדליין ומתרגלים לעבוד בקצב מהיר מדי עבורם, ולא משנה כמה יותר מהר יכתבו את הפיצ'רים לאורך זמן זה תמיד יהיה יותר לאט.
קל ליפול למעגל הזה. פעם אחת דילוורת פיצ'ר יחסית מהר וכולם היו שמחים, ואולי באותו רגע לא שמת לב לבעיות שאותו פיצ'ר יצר. פעם שניה כבר מצפים ממך לאותו קצב, ועם הזמן הציפיה מסביב ומעצמך עובדת לרעתך.
המפתח החוצה זו הטכניקה. ככל שנקפיד לעבוד על טכניקה טובה יותר לפיתוח (בדיקות אוטומטיות, תיעוד, ארגון מחדש של הקוד במקום העתקה, פיתרון בעיות אמיתיות במקום אבסטרקציות מיותרות, ביצועים), כך נגלה שקצב העבודה יורד. וזה מצוין, כי כשהטכניקה טובה הקצב יבוא בקצב שלו.
1 420
היום למדתי לא לסמוך על הקומפיילר של סקאלה
אין דבר יותר מתסכל מלבנות על הקומפיילר שיזהה בשבילך שגיאות בטיפוסים ולגלות שגם לו אין מושג. היום למדתי שבסקאלה זה יכול לקרות בקלות באזורים של העבודה המשותפת בין סקאלה ו Java. הנה דוגמה קצרה וטפשית שחבל שאף אחד לא סיפר לי עליה כשהתחלתי ללמוד סקאלה -
import scala.jdk.CollectionConverters._
val m = Map("a" -> 10)
m.asJava.get("a", 2, 3)
התוצאה היא 0, למרות ש a נמצא במפה. אפשר לטעון ששני הפרמטרים האחרים בלבלו אותו אבל האמת שגם אם נעביר את המחרוזת a לכל הפרמטרים נקבל את אותו 0. הבעיה האמיתית כאן היא ש get של java.util.Map מצפה לקבל פרמטר אחד. העברת יותר פרמטרים לפונקציה היא טעות שהיתה צריכה להתגלות בזמן קומפילציה.
צריך להגיד - מה שמתסכל בסיפור הזה הוא שהקומפיילר כן מזהה טעויות דומות אחרות, למשל הקוד הזה לא מתקמפל כי מנסים להעביר מספר פרמטרים לא נכון לפונקציה של Java:
val m = Map("a" -> 10)
m.asJava.getOrDefault("a", 1, 2)
-- Error: ----------------------------------------------------------------------
2 |m.asJava.getOrDefault("a", 1, 2)
| ^
|too many arguments for method getOrDefault in trait Map: (x$0: Object, x$1: V): V
1 error found
אבל כשקומפיילר תופס בעיות רק ב 99% מהמקרים זה לפעמים יותר מתסכל מאשר שלא יתפוס בעיות בכלל.
اکنون در دسترس! پژوهش تلگرام ۲۰۲۵ — مهمترین بینشهای سال 
