ToCode
Відкрити в Telegram
1 417
Підписники
-224 години
-37 днів
-630 день
Архів дописів
1 417
למה לא להעתיק קוד מ Stack Overflow?
אחת העצות הישנות ביותר בספר היתה "העזרו באינטרנט כדי ללמוד ובשכל כדי ליישם". למרות שיש המון קטעי קוד שעובדים מפתחים למדו בדרך הקשה שהעתקה עיוורת שלהם לפרויקט מובילה לקוד קשה לתחזוקה.
וזה לא בגלל שקטעי הקוד שמצאנו באינטרנט היו גרועים. הבעיה היתה שיש דרכים שונות לחבר קטעי קוד אחד לשני והחיבורים הם מאוד ספציפיים ולכן לא הופיעו באינטרנט.
ועכשיו הגיע AI וחברים מספרים לי כמה שהוא מדהים ואיך אפשר לכתוב מערכות שלמות מהר ואני מודה שזה קצת מרגיש כמו לחזור לאותן שיחות ואותן בעיות - קוד עם חלוקה לא נכונה לפונקציות, אבסטרקציות לא נכונות או לא מספיק יעילות, יותר מדי קוד כשאפשר היה להסתפק בפחות, בקיצור קוד שמי שיצטרך לתחזק אותו יצטרך לעבוד מאוד קשה. ואני מבין את החשיבה, ה AI כתב תנו ל AI לתחזק.
אבל ה AI לא כתב. זה אתה כתבת. ובכל מקרה ה AI לא יצליח לתחזק את זה.
מפתחים עובדים מאוד קשה כדי שנוכל לכתוב קוד שניתן לתחזוקה. באופן עקבי אנחנו חולמים על היום שנוכל לכתוב "פעם אחת" קוד שעובד ולא לתחזק אותו, זה היה עם תכנות מונחה עצמים כשחשבנו שנוכל להחליף רק את המימוש ולשמר את הממשק, זה קרה עם מיקרו סרביסס כשקיווינו שנוכל לשכתב רק סרביס בודד כל פעם שהוא יתיישן, זה קרה עם שפות דינמיות כשהיינו בטוחים שאנחנו יכולים להסתדר בלי הגדרות טיפוסים וזה קורה עכשיו עם ה AI.
כן AI יכול לממש מהר קטעי קוד שלי היה לוקח יותר זמן לכתוב. כן AI עוזר לי ללמוד טכנולוגיות חדשות ולהבין איך דברים עובדים מהר יותר. וכן AI יכול לעזור לי כשאני שוכח תחביר ספציפי של משהו שכתבתי פעם אחרונה לפני שנה ואני יודע איך זה צריך להיראות בגדול. אבל כשאני נותן ל AI לכתוב קוד שאני לא מבין זה לא ה AI שיצטרך או יוכל לתחזק אותו.
מה כן?
1. מתכננים בעצמנו איך דברים צריכים להיראות ולעבוד (הארכיטקט זה אתה).
2. נותנים ל AI למלא מימושים, ועוברים על הקוד שלו לוודא שאנחנו מבינים מה שכתוב שם.
3. מנקים ומארגנים מחדש את הקוד למבנים הגיוניים שאנחנו נוכל לתחזק אחרי שה AI מסיים לירות.
חשבו על כל מוצרי התוכנה שאתם אוהבים - אלה מערכות אמינות ומהירות שנעים לעבוד איתן. זה סוג המערכות שנשמח לקבל.
1 417
תגית style בתוך קומפוננטות
האם ריאקט החליטו בגירסה 19 להילחם בטיילווינד? אני מקווה שלא, אבל בכל מקרה הנה פיצ'ר מעניין שהם בנו שיכול לעזור לכם לסדר הגדרות עיצוב באתרים פשוטים.
איך זה עובד
ריאקט מאפשר לנו לכתוב תגית style בתוך JSX של קומפוננטה כדי לשתול קוד CSS בתור Inline Style במסמך. נזכור שאם בתוך HTML אנחנו כותבים תגית style, אפילו מחוץ ל head, אז הדפדפן עדיין מתיחס לכללי ה CSS שמוגדרים שם. הקוד הבא בתוך קומפוננטה מוסיף תגית style כזו:
export default function Main() {
return (
<>
<style>
{\
h1 { color: red }
body { background: yellow; }
a { color: blue }
\}
</style>
<h1>Hello World</h1>
</>
)
}
הפעילו את הקומפוננטה בתוך עמוד ותוכלו לראות את תגית ה style ואת השפעתה על הצבעים בעמוד.
הבעיה עם הדבר הזה היא שאם תגית style כתובה בתוך קומפוננטה אז היא תופיע מספר פעמים בעמוד (לפי מספר הפעמים שהקומפוננטה מודבקת לעמוד).
לכן ריאקט מאפשר לנו להגדיר מזהה לכל תגית style ואם אנחנו מגדירים מזהה וקדימות לתגית style אז ריאקט יעביר את התגית ל head של המסמך. אם לשתי תגיות style איפשהו בעמוד יש את אותו מזהה אז רק אחת מהן תופיע בראש העמוד. אנחנו מגדירים מזהה עם מאפיין href וסדר קדימויות עם מאפיין precedence:
export default function Main() {
return (
<>
<style href="main" precedence="medium">
{\
h1 { color: red }
body { background: yellow; }
a { color: blue }
\}
</style>
<h1>Hello World</h1>
</>
)
}
מה אפשר לבנות עם זה
אם אתם מעדיפים לעבוד עם CSS גלובאלי אבל לא יודעים מראש איזה קומפוננטות יהיו בעמוד ולא רוצים להוסיף CSS לעמוד שבסוף לא ישתמשו בו תוכלו להשתמש במנגנון style של ריאקט כדי לצמצם כפילויות ולהוריד CSS שאינו בשימוש. כך זה יעבוד:
1. כתבו קומפוננטות בסיסיות לדוגמה כפתור, ובהן הגדירו את ה CSS שהן צריכות בתור תגית style עם href וסדר קדימות.
2. בקומפוננטות הגדולות יותר שלכם השתמשו בקומפוננטות הבסיסיות ככל שתרצו.
3. באופן אוטומטי כל כללי העיצוב שמגיעים מהקומפוננטות הבסיסיות ייכנסו לעמוד בתור Inline Style. אין בעיה כשטוענים קומפוננטה בסיסית מספר פעמים כי ריאקט ימחק את כלל ה CSS הכפול.
ארכיטקטורה זו של כתיבת כל קוד ה CSS בקומפוננטות בסיס היא דרך טובה לגשת לעיצוב קומפוננטות בריאקט בכל מקרה. אישית אני מעדיף לעצב את קומפוננטות הבסיס באמצעות Tailwind כי כך אני מבטיח שאין התנגשות במזהים או בשמות קלאסים שבחרתי.1 417
טיפ גיטהאב: שינוי Base ב Pull Request
שלחתם Pull Request לפרויקט שזז מהר. עד שהבן אדם בצד השני הספיק לשלוח הערות השינוי שלכם עכשיו 4 גירסאות מאחור. בשביל שיהיה מסודר אתם עושים ריבייס לגירסה הכי חדשה ו - הפתעה, גיטהאב מציג את ה diff בין הגירסה שלכם לגירסה מולה יצרתם את ה Pull Request כשהתחלתם. כלומר כל השינויים ב main שקרו באותם 4 קומיטים שנכנסו אליכם לגירסה (כי עשיתם את הריבייס) מופיעים עכשיו כחלק מה diff של הגירסה שלכם ב Pull Request.
מה עושים?
הפיתרון הוא בכפתור קצת מבלבל בממשק של גיטהאב במסך Pull Request: לוחצים על Edit ואז שם הענף אליו אתם רוצים לשלב את הגירסה הופך לתיבת בחירה. אתם לוחצים על התיבה, בוחרים באפשרויות שנפתחו את אותו ענף שכבר מסומן ואז מופיע דיאלוג חדש עם הכותרת
> Are you sure you want to change the base?
רגע, מה? לא שיניתי כלום רק בחרתי את מה שכבר היה מסומן לא? לא בדיוק. ברגע שיוצרים Pull Request גיטהאב מדביק את ה PR שלכם לקומיט מסוים, אפילו שבחרתם לשלב את ה PR לתוך ענף. כשגיטהאב מראה בממשק שאתם מנסים לשלב את ה PR לתוך base: main הוא בעצם מתכוון שאתם מנסים לשלב לתוך ענף main בקומיט מסוים (וקצת ישן). שינוי מ main ל main הוא בעצם שינוי הקומיט שביחס אליו נוצר ה PR לקומיט העדכני ביותר בענף main. בצורה כזאת רשימת הקומיטים והשינויים תתחיל בקומיט האחרון ב main ולא בקומיט המקורי ממנו נוצר ה PR.
1 417
מתי ואיך אני משתמש ב AI
שאלה אמיתית - אתם מרגישים שאתם מרגישים יותר מדי או פחות מדי ב AI? ואם פחות מדי, למה אתם לא משתמשים בו יותר? הנה רשימת המקרים בהם אני משתמש ב AI במהלך הפיתוח ו Best Practices שמצאתי לכל מקרה:
1. כשאני לא יודע עדיין איך דברים עובדים - אם אני צריך לבנות משהו שאני עדיין לא יודע איך הוא עובד אני לפעמים אלך ל AI כדי לקבל מספר אפשרויות ולהבין בגדול על מה צריך לקרוא. למדתי להיזהר מהתשובות הראשונות של ה AI כי הוא לא יודע לבחור מה הדרך הטובה או המודרנית ביותר לפתור בעיה, אבל הוא כן יכול לתת סקירה של מושגי מפתח שקשורים לבעיה. לדוגמה, אם אני צריך להוציא מידע מיוטיוב אבל לא עבדתי אף פעם עם ה API שלהם אני יכול לבקש מה AI הסבר כללי איך זה עובד וגם דוגמאות קוד (ורק זוכר להמשיך לקרוא מחוץ ל AI בשביל לדייק את הדברים).
2. כשאני כותב קוד ויודע מה אני רוצה - אם אני בונה סכימה לדריזל ואני יודע איזה טבלה אני רוצה ואיזה עמודות זה זמן מושלם ללחוץ Command I ב VS Code ולבקש מ Copilot לכתוב את הבלוק שמוסיף את הטבלה לסכימה, וכך חוסך לי לבדוק מה השם המדויק של הפונקציה.
3. כשאני צריך לשנות פורמט של מידע או אם יש לי JSON וצריך לבנות ממנו ממשק לטייפסקריפט, או כשצריך לעטוף API בקוד שלי ולהוסיף טיפוסים.
4. כשאני כותב קוד שאני יודע שאזרוק רק בשביל לראות איך דברים נראים, למשל אני יכול להיעזר ב AI כדי לכתוב בר עליון לאיזה ממשק גרפי וכך להבין מהר אם זה בכלל משהו שאני רוצה. אם השתכנעתי שאני רוצה את הפיצ'ר בדרך כלל אזרוק את הקוד הראשוני שה AI יצר, אבנה "מסגרת" לקוד ואתן ל AI למלא את הקוד בתוך המסגרת.
5. באופן כללי קוד אמיתי ש AI כותב לתוך המערכת יעבוד הרבה יותר טוב אם אני נותן לו למלא קוד בתוך מסגרת ולא לבנות את המסגרת בעצמו.
6. כשאני כותב קוד להיבט שאינו ליבתי במערכת ואני בסדר עם זה שהקוד לא יהיה מלהיב או ניתן לתחזוקה, לדוגמה אני מוכן לתת ל AI לכתוב Github Action שמריץ בדיקות אוטומטיות. אם תהיה בעיה ממילא אפשר לבקש מ AI לכתוב אחד חדש ולזרוק את הקובץ הקיים. כל עוד אין תלויות הכל בסדר.
7. כתיבת תיעוד, בדיקות והודעות קומיט.
8. קבלת Code Review - לפני קומיט אני מפעיל
git diff ושולח את הפלט ל AI כדי לקבל סיכום מסודר של מה שעשיתי והצעות לשיפור או תיקונים.
ומה אתכם? באיזה מצבים אתם משתמשים או לא משתמשים ב AI? ואם הייתם רוצים להשתמש יותר, מה הסיבות המרכזיות בגללן אתם עדיין מקודדים בלעדיו?
נ.ב. הג'מיני החדש באמת מטורף.1 417
def step
@guard.step
if @matrix[@guard.coords] == '#'
@guard.back
@guard.turn
end
@time_loop = true if @visited.include?([@guard.coords, @guard.heading])
@visited << [@guard.coords, @guard.heading] if @matrix.key?(@guard.coords)
end
def run
step_count = 0
turn_count = 0
while (@matrix.key?(@guard.coords) && !@time_loop) do
guard_before = @guard.coords
step
step_count += 1
turn_count += 1 if @guard.coords == guard_before
end
# Return important values - makes testing easier
[@time_loop, step_count, turn_count]
end
def reset
@time_loop = false
@visited = Set.new
@guard = Guard.new(Complex(@guard_start[0], @guard_start[1]))
@visited << [@guard.coords, @guard.heading]
end
end
def find_time_loops(room)
start = room.visited.first
room.run
# Extract the coordinates of visited positions, excluding the start
visited_positions = (room.visited - Set.new([start])).map(&:first).to_set
# Find positions that cause time loops
time_loops = visited_positions.select do |coords|
original_value = room.matrix[coords]
room.matrix[coords] = '#'
room.reset
result = room.run.first # Check if a time loop occurred
# Restore the original value and reset the room
room.matrix[coords] = original_value
room.reset
result
end
time_loops.length
end
* Main execution *
room = Room.new
puts find_time_loops(room)1 417
visited << [guard.coords, guard.heading]
end
בשביל כל ניסוי יצרתי Room חדש ובו שמתי את המכשול בנקודה אחרת על המסלול. הבעיה היא שיצירת Room היתה כרוכה בקריאת כל קובץ הקלט ובניית המטריצה המלאה, וזו פעולה כבדה. כשניסיתי להריץ את הקוד הוא לא הגיע לסיום, ואז אני נכנסתי ללולאה אינסופית לחפש אופטימיזציות.
בסופו של דבר ובעזרת פיזור נכון של הודעות הדפסה בקוד גם אצלי נפל האסימון וראיתי שלא צריך להתחיל לגמרי מאפס בשביל לבדוק כל נקודה על המסלול ואפשר להשתמש באותה מטריצה. האופטימיזציה שברה את כל ה Object Oriented שניסיתי לכתוב אבל אפשרה לקוד להגיע לתוצאה בתוך פחות מדקה - לא מלהיב אבל מספיק טוב בשביל להתקדם. זה הקוד:
require 'set'
class Guard
attr_accessor :position, :heading
def initialize(position)
self.heading = -1 + 0i
self.position = position
end
def step
self.position = position + heading
end
def turn
self.heading = Complex(heading.imag, -heading.real)
end
def back
self.position = position - heading
end
def coords
[position.real, position.imag]
end
end
class Room
attr_accessor :matrix, :guard, :visited, :time_loop, :guard_start
def initialize()
self.time_loop = false
self.visited = Set.new
self.matrix = File.read('input.txt').lines.each_with_index.flat_map do |line, line_index|
line.chomp.split('').each_with_index.map do |char, column_index|
if char == '^'
self.guard_start = [line_index, column_index]
self.guard = Guard.new(Complex(line_index, column_index))
{ [line_index, column_index] => '.' }
else
{ [line_index, column_index] => char }
end
end
end.reduce({}) {|acc, val| acc.merge(val) }
visited << [guard.coords, guard.heading]
end
def step
guard.step
if matrix[guard.coords] == '#'
guard.back
guard.turn
end
self.time_loop = true if visited.include?([guard.coords, guard.heading])
visited << [guard.coords, guard.heading] if matrix.key?(guard.coords)
end
def run
step_count = 0
turn_count = 0
while (matrix.key?(guard.coords) && !time_loop) do
guard_before = guard.coords
step
step_count += 1
if guard.coords == guard_before
turn_count += 1
end
end
end
end
room = Room.new
start = room.visited.first
room.run
time_loops = (room.visited - Set.new([start])).map(&:first).to_set.filter do |coords|
room.matrix[coords] = '#'
room.run
time_loop = room.time_loop
room.time_loop = false
room.matrix[coords] = '.'
room.visited = Set.new
room.guard = Guard.new(Complex(room.guard_start[0], room.guard_start[1]))
time_loop
end
pp time_loops.length
וזה אחרי ש Claude עבר עליו והעביר את לוגיקת האיפוס למתודה בתוך Room (הצעה של קלוד):
require 'set'
class Guard
attr_accessor :position, :heading
def initialize(position)
@heading = -1 + 0i # Using instance variables consistently
@position = position
end
def step
@position += @heading # Using += for cleaner code
end
def turn
@heading = Complex(@heading.imag, -@heading.real)
end
def back
@position -= @heading # Using -= for cleaner code
end
def coords
[@position.real.to_i, @position.imag.to_i] # Convert to integers to avoid floating point comparison issues
end
end
class Room
attr_accessor :matrix, :guard, :visited, :time_loop, :guard_start
def initialize
@time_loop = false
@visited = Set.new
parse_input_file
@visited << [@guard.coords, @guard.heading]
end
def parse_input_file
@matrix = {}
File.readlines('input.txt', chomp: true).each_with_index do |line, line_index|
line.chars.each_with_index do |char, column_index|
coords = [line_index, column_index]
if char == '^'
@guard_start = coords
@guard = Guard.new(Complex(line_index, column_index))
@matrix[coords] = '.'
else
@matrix[coords] = char
end
end
end
end1 417
פיתרון Advent Of Code יום 6 ב Ruby - היום לגמרי לא ראיתי את זה
תרגיל יום 6 של Advent Of Code הזכיר לי ראיון עבודה מהעבר: המראיין שאל אותי שאלה קלה ושידעתי את התשובה אליה, אבל מכל מיני סיבות באותו רגע של הראיון לא ראיתי את זה. קשה להסביר מה עבר לי בראש באותו זמן, אבל אני זוכר טוב את הוודאות שהרגשתי לגבי הקושי של השאלה (באותו רגע נראתה לי ממש מסובכת) ולגבי חוסר היכולת שלי לפתור אותה. אפילו שהמראיין ניסה לדחוף אותי לפיתרון הדבר היחיד שיכולתי לבקש היה לנסות שאלה אחרת. בסופו של דבר הצלחתי לענות על מספיק שאלות אחרות בשביל לעבור את אותו ראיון.
בכל מקרה כשהסתכלתי על החלק השני של תרגיל יום 6 הרגשתי בדיוק באותה צורה. חיפשתי אינסוף דרכים חכמות לפתור את הבעיה, כשהפיתרון הפשוט היה לי מול העיניים כל הזמן. בואו נראה את זה.
האתגר
נתונה מפה בפורמט טקסטואלי שאופייני לאתגרי Advent Of Code:
....#.....
.........#
..........
..#.......
.......#..
..........
.#..^.....
........#.
#.........
......#...
סימן ה ^ מייצג שומר שמתקדם לכיוון שהחץ מראה והסולמיות הן מכשולים. כששומר נתקל במכשול הוא מסתובב ימינה וכשהוא יוצא מהמפה הוא מסיים את הסיור שלו.
חלק 1 - הסימולטור
השאלה הראשונה היתה בכמה משבצות הוא יבקר במהלך הסיור. נו, אנחנו יודעים לקודד אז נפתח קובץ רובי ונכתוב סימולטור לצעדי השומר:
require 'set'
class Guard
attr_accessor :position, :heading
def initialize(position)
self.heading = -1 + 0i
self.position = position
end
def step
self.position = position + heading
end
def turn
self.heading = Complex(heading.imag, -heading.real)
end
def back
self.position = position - heading
end
def coords
[position.real, position.imag]
end
end
class Room
attr_accessor :matrix, :guard, :visited
def initialize()
self.visited = Set.new
self.matrix = File.read('input.txt').lines.each_with_index.flat_map do |line, line_index|
line.chomp.split('').each_with_index.map do |char, column_index|
if char == "^"
self.guard = Guard.new(Complex(line_index, column_index))
{[line_index, column_index] => '.' }
else
{[line_index, column_index] => char }
end
end
end.reduce({}) {|acc, val| acc.merge(val) }
self.visited << guard.coords
end
def step
guard.step
if matrix[guard.coords] == "#"
guard.back
guard.turn
end
visited << guard.coords if matrix.key?(guard.coords)
end
def run
step while matrix.key?(guard.coords)
end
end
room = Room.new
room.run
pp room.visited.length
ונכון קוד מונחה עצמים מרגיש קצת מיושן ב 2025 אבל בכל זאת אנחנו ב Ruby ומשום מה מחלקה שמייצגת את השומר נראתה לי רעיון טוב באותו רגע.
חלק 2 - האתגר
בחלק השני אנחנו צריכים להיות יצירתיים ולחפש מקומות בהם נוכל לחסום את השומר כדי לתקוע אותו בלולאה אינסופית. הדוגמה מהסיפור היתה לשים מכשול ממש משמאל לנקודת ההתחלה ואז מקבלים את המסלול האינסופי:
....#.....
....+---+#
....|...|.
..#.|...|.
....|..#|.
....|...|.
.#.O^---+.
........#.
#.........
......#...
אבל יש עוד אפשרויות והמשימה היא למצוא את כל האפשרויות שיתקעו את השומר בלולאה אינסופית. ופה אני מודה שאני קצת נתקעתי בלולאה אינסופית - הרעיון הראשון שלי היה פיתרון נאיבי, כלומר לקחת את כל הנקודות במסלול, לנסות לשים מכשול בכל נקודה ולראות אם השומר נתקע בלולאה אינסופית. מה שהכשיל אותי היה פונקציית ה initialize בקוד מונחה העצמים המופלא שכתבתי:
def initialize()
self.time_loop = false
self.visited = Set.new
self.matrix = File.read('input.txt').lines.each_with_index.flat_map do |line, line_index|
line.chomp.split('').each_with_index.map do |char, column_index|
if char == '^'
self.guard_start = [line_index, column_index]
self.guard = Guard.new(Complex(line_index, column_index))
{ [line_index, column_index] => '.' }
else
{ [line_index, column_index] => char }
end
end
end.reduce({}) {|acc, val| acc.merge(val) }1 417
זה לא תורמוס
היו ימים שבשביל לזהות פרח היה צריך לפתוח מגדיר צמחים ולחפש בין תמונות דהויות. היום בעידן ה AI מספיק לצלם את הפרח, לשאול את החבר מה יש בתמונה ולהחזיק אצבעות. כשהוא כותב תורמוס על תמונה של בן חצב יקינתוני זאת אחריות שלנו לזהות את הטעות (רק בשביל לקבל ממנו איזה "אתה צודק מאוד!"). כשעוברים לפייתון אין לו בעיה לזרוק לי בביטחון את המימוש הבא לחישוב סדרת פיבונאצ'י בשורה אחת:
fibonacci = lambda n, a=0, b=1: [a := b, b := a + b][0] if n else None
נראה יופי אבל כמו התורמוס, התוצאה אינה מספר מסדרת פיבונאצ'י.
וגם בתכנות הטעויות יכולות להיות יותר עדינות, ופה זה מתחיל להיות מטריד. הנה עוד שני מימושים שקיבלתי, תחילה:
from functools import lru_cache
fib = lru_cache(None)(lambda n: n if n < 2 else fib(n-1) + fib(n-2))
print(fib(10)) # Output: 55
ואז הצעה נוספת ש"משתמשת באיטרציות כדי לשפר ביצועים":
fib = lambda n, a=0, b=1: a if n == 0 else fib(n-1, b, a+b)
print(fib(10)) # Output: 55
ואתם צודקים, אין פה איטרציות ובהשוואה ל LRU Cache גם אין פה שיפור ביצועים.
עכשיו הסיפור הוא לא תורמוסים ולא פיבונאצ'י. המעבר לתוכנות מבוססות GPT הוא גם מעבר למערכות שעושות טעויות. לא "באגים" כמו שאנחנו רגילים מאז ומעולם אלא טעויות רנדומליות שמקורן במבנים בסיסיים לא אמינים. גם לזה עוד נצטרך להתרגל.1 417
בדיקות null ב JavaScript
בפוסט היום נראה שלושה אופרטורים לבדיקת Null Values והתמודדות עם ערכים ריקים.
למה להתמודד עם ערכים ריקים
תוכניות JavaScript משתמשות בערכים ריקים כדי לייצג שגיאה או מידע חסר. בכל תוכנית שאנחנו כותבים נרצה להתמודד עם שגיאות או מידע חסר בתוך חישובים מורכבים. השאלה היא מה עושים עם המידע החסר או השגיאה מחישובים קודמים בכתיבת חישוב מורכב. האפשרויות הן:
1. לזהות שיש מידע חסר ולעצור את החישוב המורכב.
2. להמשיך את החישוב עם "המידע החסר", ומי שצריך את התוצאה של החישוב כבר יתמודד עם הבעיה.
נדמיין פונקציה שמקבלת מערך ומספר ומחזירה את מספר המספרים מהמערך הגדולים מהמספר שקיבלה:
function largerThan(items, start) {
return items.filter(x => x > start).length;
}
מה קורה אם מנסים להפעיל אותה אבל מעבירים רק את items בלי start? מה אנחנו רוצים שיקרה? הגישה הראשונה היא לזהות בתוך הפונקציה שחסר מידע ולעצור את החישוב באמצעות זריקת קוד שגיאה. גישה זו עובדת כשאנחנו יודעים שהקוד החיצוני הפעיל את largerThan בתוך בלוק try/catch ומוכן להתמודד גם עם מסלול ההצלחה וגם עם מסלול הטעות.
גישה שניה אומרת שננסה להתמודד באמצעות הכנסת הערך החסר לתוך החישוב, כלומר נחזיר למי שקרא לנו את הערך 0 או אפילו את הערך null, מתוך הבנה שמי שקרא לפונקציה יוכל לטפל בחישוב עם הערך החסר. התמודדות כזאת עם ערכים ריקים יכולה לאפשר כתיבת קוד ממוקד יותר שיש לו רק מסלול פעולה אחד.
בקוד הדוגמה אגב אם אנחנו מעבירים רק פרמטר אחד לפונקציה היא תחזיר 0 וכך גם אם נעביר טקסט או undefined בתור הפרמטר השני, אבל אם נעביר null היא תהפוך את ה null ל 0 ותחזיר את מספר האיברים במערך שגדולים מאפס, וזו התנהגות קצת מבלבלת.
גירסת ES2020 הוסיפה שתי פעולות לעבודה עם ערכי null ב JavaScript מבוססות על סימן השאלה.
אופרטור Nullish coalescing
אופרטור ?? נקרא Nullish Coalescing ותפקידו לבחור את הערך שאינו null מבין שני ערכים. נוכל להשתמש בו באופן הבא:
function printTimes(text, aTimes) {
const times = aTimes ?? 5;
for (let i=0; i < times; i++) {
console.count(text);
}
}
הפונקציה תדפיס את הטקסט שהיא קיבלה times פעמים, או אם times אינו מוגדר או null היא תשתמשת בברירת המחדל 5. נשים לב שאם מעבירים ערך 0 או false ל aTimes אז הערך של times יהיה 0 והטקסט לא יודפס.
השמה ובדיקת null
אפשר לשלב את ?? עם אופרטור השמה וזה נראה כך:
function printTimes(text, times) {
times ??= 5;
for (let i=0; i < times; i++) {
console.count(text);
}
}
התוצאה זהה - אם times היה null או לא מוגדר נשמור בו את הערך 5, בכל מצב אחר הוא ישמור על ערכו.
שרשור אופציונאלי Optional Chaining
פעולה אחרונה שקשורה לעבודה עם ערכים ריקים בחישוב ולכן הכנסתי אותה לרשימה זו (למרות שהיא לא לגמרי קשורה לפעולות על null) נקראת Optional Chaining. הרעיון שלה הוא לאפשר לנו להמשיך חישוב גם כשחסר שדה באוביקט, מתוך הנחה שבהמשך התוכנית מישהו כבר ידע להתמודד עם הערך הריק.
נתבונן בדוגמה הבאה:
const data = {
x: { times: 7 },
y: { times: 2 },
};
const f = (what) => printTimes(what, data[what].times);
אפשר להשתמש בפונקציה כדי להדפיס את x או y באופן הבא:
f('x');
f('y');
אבל אם ננסה להשתמש בה כדי להדפיס משהו שלא נמצא באוביקט למשל את המחרוזת z נקבל שגיאה - הערך של data[what] הוא undefined ולכן אי אפשר לגשת לשדה times שלו.
פעולת Optional Chaining מאפשרת לדלג על השגיאה הזאת ובמצב בו מנסים לגשת לשדה של undefined היא מחזירה undefined במקום לזרוק שגיאה. הפעולה מסומנת עם ?. ובדוגמה שלנו תיראה כך:
const f = (what) => printTimes(what, data[what]?.times);
f('z');
הפעם ההדפסה האחרונה לא זורקת שגיאה ומדפיסה את הטקסט 5 פעמים - הפונקציה printTimes נקראת עם ערך undefined במשתנה השני ותשתמש בברירת המחדל שלה 5 כדי להדפיס.1 417
היום נתתי ל Aider לכתוב פיצ'ר חדש לאתר
הבוקר נתתי ל Aider לכתוב פיצ'ר חדש לאתר - עכשיו כשמסתכלים על תוכן העניינים של קורס מתוך מסך של שיעור, השיעור הנוכחי מופיע בצבע קצת אחר וככה אפשר לדעת איפה אנחנו נמצאים. זה פיצ'ר שרציתי הרבה זמן אבל איכשהו לא הגעתי לשבת לכתוב אותו. ואז Aider נתן לי הזדמנות. אגב גם תיעדתי את המשחק איתו בוידאו כאן:
https://www.youtube.com/watch?v=thSABbP1rF4&feature=youtu.be
עכשיו הסיכום:
1. אני לא חושב ש AI ייקח לנו את העבודה בזמן הקרוב.
2. הבעיה הכי גדולה עם AI זה הביטחון העצמי המופרז שלו. אנחנו רגילים להאמין לדברים שכתובים באינטרנט, אבל AI משנה את זה. בכל עבודה עם AI אנחנו צריכים להתחיל מרף חשדנות מאוד גבוה.
3. קונטקסט הוא מלך. ככל שנמקד את ה AI במשימה ספציפית סיכויי ההצלחה משתפרים.
4. לפעמים צריך לחזור אחורה קומיט אחד ולתת לו לנסות שוב.
אין ספק שהייתי כותב את הפיצ'ר הזה מהר יותר לבד, אבל בסך הכל נהניתי מהמשחק ואולי הוא יתגלה מועיל בפיצ'רים יותר מסובכים.
Вже доступно! Дослідження Telegram за 2025 — головні інсайти року 
