en
Feedback
ToCode

ToCode

Open in Telegram

טיפים ×§×¦×Ø×™× ×œ×ž×Ŗ×›× ×Ŗ×™× ×ž××Ŗ ינון פרק

Show more
1 420
Subscribers
+124 hours
+17 days
-430 days
Posts Archive
ToCode
1 420
×ž×“×Ø×™×š Next.JS - פרק 1 ×”×Ŗ×§× ×” ×•×¤×Ø×•×™×§×˜ ×Ø××©×•×Ÿ השבוע הבלוג יצא ×œ×¤×’×Ø×” אבל כדי לא ×œ×”×©××™×Ø את תיבת המייל שלכם ריקה מדי אשלח במהלך התקופה ×ž×“×Ø×™×š next.js שכתבתי ×ž×Ø××©. מקווה ×©×Ŗ×ž×¦××• אותו מועיל ונחזור אחרי חנוכה בכוחות מחודשים. מהו Next.JS נקהט ג'יי אה הוא הביבה ×œ×¤×™×Ŗ×•×— יישומי Full Stack ×ž×‘×•×”×”×Ŗ ×Ø×™××§×˜, ×”×ž××¤×©×Ø×Ŗ לנו ×‘×¤×Ø×•×™×§×˜ אחד ×œ×›×Ŗ×•×‘ גם את קוד צד השרת וגם את קוד צד הלקוח. הדגש של ×”×¤×Ø×™×™×ž×•×•×Ø×§ הוא על ×§×œ×•×Ŗ בניית ×¤×Ø×•×™×§×˜ ×•××™× ×˜×’×Ø×¦×™×” חזקה בין קוד צד שרת לקוד צד לקוח, מה שהופך אותו ×œ×‘×—×™×Ø×” מאוד קלה ×œ×¤×Ø×•×™×§×˜×™ ×Ø×™××§×˜ חדשים. יותר מזה, next.js נוצר על ידי חברת vercel ויש לו כלי אוטומטי ל deployment על הענן שלהם, כך שאפשר להביא את ×”×¤×Ø×•×™×§×˜ ×ž×ž×›×•× ×Ŗ הפיתוח ×œ××™× ×˜×Ø× ×˜ בכמה דקות. ×¤×Ø×•×™×§×˜ ×Ø××©×•×Ÿ בשביל ×œ×™×¦×•×Ø ×¤×Ø×•×™×§×˜ next.js ×Ø××©×•×Ÿ אני ×¦×Ø×™×š רק ×œ×”×Ŗ×§×™×Ÿ את node.js ואז להפעיל את הפקודה:
npx create-next-app@latest 01-intro
×•×œ×¢× ×•×Ŗ על ×ž×”×¤×Ø ×©××œ×•×Ŗ (אני בחרתי בכולן את ברירות המחדל). זה ×ž×©×ž×¢×•×Ŗ×™×Ŗ יותר ×ž×”×™×Ø וקל מלהקים קונפיגורציית וובפאק בעצמנו וזה אפילו יותר קל ×ž×™×¦×™×Ø×Ŗ ×¤×Ø×•×™×§×˜ עם vite. אחרי יצירת ×”×¤×Ø×•×™×§×˜ אפשר ×œ×¤×Ŗ×•×— שרת ×œ×¤×™×Ŗ×•×— מקומי על ידי:
cd 01-intro
npm run dev
פתיחת ×”×¤×Ø×•×™×§×˜ שנוצר בהביבת פיתוח תראה לנו שיש בהך הכל שני קבצים מעניינים ×‘×™× ×Ŗ×™×™× בתיקיית app, ×”×Ø××©×•×Ÿ נקרא layout.tsx והשני נקרא page.tsx. ב layout אני מוצא את הקוד הבא:
import type { Metadata } from 'next'
import { Inter } from 'next/font/google'
import './globals.css'

const inter = Inter({ subsets: ['latin'] })

export const metadata: Metadata = {
  title: 'Create Next App',
  description: 'Generated by create next app',
}

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body className={inter.className}>{children}</body>
    </html>
  )
}
וב page.tsx את הקוד הבא:
import Image from 'next/image'

export default function Home() {
  return (
    <main className="flex min-h-screen flex-col items-center justify-between p-24">
      <div className="z-10 max-w-5xl w-full items-center justify-between font-mono text-sm lg:flex">
        <p className="fixed left-0 top-0 flex w-full justify-center border-b border-gray-300 bg-gradient-to-b from-zinc-200 pb-6 pt-8 backdrop-blur-2xl dark:border-neutral-800 dark:bg-zinc-800/30 dark:from-inherit lg:static lg:w-auto  lg:rounded-xl lg:border lg:bg-gray-200 lg:p-4 lg:dark:bg-zinc-800/30">
          Get started by editing&nbsp;
          <code className="font-mono font-bold">src/app/page.tsx</code>
        </p>
        <div className="fixed bottom-0 left-0 flex h-48 w-full items-end justify-center bg-gradient-to-t from-white via-white dark:from-black dark:via-black lg:static lg:h-auto lg:w-auto lg:bg-none">
          <a
            className="pointer-events-none flex place-items-center gap-2 p-8 lg:pointer-events-auto lg:p-0"
            href="https://vercel.com?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
            target="_blank"
            rel="noopener noreferrer"
          >
            By{' '}
            <Image
              src="/vercel.svg"
              alt="Vercel Logo"
              className="dark:invert"
              width={100}
              height={24}
              priority
            />
          </a>
        </div>
      </div>

      <div className="relative flex place-items-center before:absolute before:h-[300px] before:w-[480px] before:-translate-x-1/2 before:rounded-full before:bg-gradient-radial before:from-white before:to-transparent before:blur-2xl before:content-[''] after:absolute after:-z-20 after:h-[180px] after:w-[240px] after:translate-x-1/3 after:bg-gradient-conic after:from-sky-200 after:via-blue-200 after:blur-2xl after:content-[''] before:dark:bg-gradient-to-br before:dark:from-transparent before:dark:to-blue-700 before:dark:opacity-10 after:dark:from-sky-900 after:dark:via-[#0141ff] after:dark:opacity-40 before:lg:h-[360px] z-[-1]">
        <Image
          className="relative dark:drop-shadow-[0_0_0.3rem_#ffffff70] dark:invert"

ToCode
1 420
חידת ×’×Ø×ž×œ×™×Ÿ - Coalesce הקוד הבא היה ××ž×•×Ø לבדוק אם קיים ×•×Ø×§×˜×§×” עם ×”×¢×Ø×š bob במאפיין name, ואם לא קיים ×œ×™×¦×•×Ø חדש, אבל הוא לא עושה בדיוק את מה שרצינו. מה עושה הקוד, מה הבעיה ואיך נתקן אותו?
g
  .V()
  .coalesce(
    __.has('person', 'name', 'bob'),
    __.addV('person').property('name', 'bob')
  )
  .iterate()
הבעיה בקוד הפקודה coalesce של ×’×Ø×ž×œ×™×Ÿ ×ž×§×‘×œ×Ŗ ×ž×”×¤×Ø אפשרויות ×œ×”×Ŗ×§×“×ž×•×Ŗ בגרף ×•×ž×‘×¦×¢×Ŗ ××•×Ŗ×Ÿ לפי ההדר עד שמגיעה ×œ××¤×©×Ø×•×Ŗ ×©×ž×—×–×™×Ø×” ×¦×•×ž×Ŗ ×Ŗ×§×™×Ÿ. לכן ×œ×›××•×Ø×” הקוד היה ××ž×•×Ø ×œ×”×Ŗ×—×™×œ מכל ×”×¦×ž×Ŗ×™× בגרף, למצוא ×¦×•×ž×Ŗ שיש לו את המאפיין name עם ×”×¢×Ø×š bob, אם קיים ×œ×”×—×–×™×Ø אותו ואם לא קיים להגיע ל addV. אבל לא ככה זה עובד. הבעיה בקוד היא ש coalesce מופיע אחרי V() ולכן הוא יופעל על כל ×”×¦×ž×Ŗ×™× בגרף, ×›×œ×•×ž×Ø לכל ×¦×•×ž×Ŗ נבדוק בנפרד אם הוא ×ž×Ŗ××™× ל has ואם לא × × ×”×” להוהיף ×¦×•×ž×Ŗ כזה עם addV, מה ×©×™×’×Ø×•× להוהפה של המון ×¦×ž×Ŗ×™×. ×“×•×’×ž×Ŗ הרצה בהקאלה עם הקוד הזה:
@main def main() =
  val graph = TinkerGraph.open
  val g = traversal.withEmbedded(graph)

  println(g.addV("person").property("name", "a").next())
  println(g.addV("person").property("name", "b").next())

  println("--- 1")
  println(g.V().elementMap().toList)

  g.V()
    .coalesce(
      has("person", "name", "bob"),
      addV("person").property("name", "bob"))
    .iterate()

  println("--- 2")
  println(g.V().elementMap().toList)
מדפיהה את הפלט:
--- 1
[{id=0, label=person, name=a}, {id=2, label=person, name=b}]
--- 2
[{id=0, label=person, name=a}, {id=2, label=person, name=b}, {id=4, label=person, name=bob}, {id=6, label=person, name=bob}]
×›×œ×•×ž×Ø נוצרו שני ×¦×ž×Ŗ×™× חדשים עם השם bob. ×¤×™×Ŗ×Ø×•×Ÿ בעזרת fold איך ×ž×Ŗ×§× ×™×? הכי פשוט זה קודם לחפש את ×”×¦×•×ž×Ŗ ואז עליו להפעיל את coalesce. הבעיה היא שניהיון כזה עלול להיכשל אם ×”×¦×•×ž×Ŗ לא קיים ×•×œ×¢×¦×•×Ø את כל ×”×©××™×œ×Ŗ×”. לכן יש להוהיף צעד fold שאוהף את כל ×”×¦×ž×Ŗ×™× ×”×ž×Ŗ××™×ž×™× ×œ×Ø×©×™×ž×” ×•×œ×¤×Ŗ×•×— את אותה ×Ø×©×™×ž×” ×‘×Ŗ×•×š ה coalesce. או בקיצור בקוד:
@main def main() =
  val graph = TinkerGraph.open
  val g = traversal.withEmbedded(graph)

  println(g.addV("person").property("name", "a").next())
  println(g.addV("person").property("name", "b").next())

  println("--- 1")
  println(g.V().elementMap().toList)

  g.V()
    .has("person", "name", "bob")
    .fold()
    .coalesce(
      unfold(),
      addV("person").property("name", "bob"))
    .iterate()

  println("--- 2")
  println(g.V().elementMap().toList)
מה שיוצר רק ×¦×•×ž×Ŗ אחד עם השם bob. ×¤×™×Ŗ×Ø×•×Ÿ בעזרת merge בגירהאות מהפיק חדשות של ×˜×™× ×§×Ø×¤×•×¤ אפשר ×œ×”×©×Ŗ×ž×© ב merge כדי לחהוך את כל הבלאגן הזה כשאנחנו מנהים למזג ×¦×•×ž×Ŗ לפי מאפיין כמו name. הקוד נראה ככה:
  g.mergeV(Map[Object, Object](
    T.label -> "person",
    "name" -> "bob"
  ).asJava)
  .iterate()
כל עוד המיזוג הוא לפי מאפיינים שאנחנו ×ž×›×™×Ø×™× זה יהיה ×”×¤×™×Ŗ×Ø×•×Ÿ הכי טוב. ×›×©×¦×Ø×™×š למזג לפי יחהים ×œ×¦×ž×Ŗ×™× ××—×Ø×™× (לדוגמה אם קיים ×¦×•×ž×Ŗ person שהוא friend של bob) אז נצטרך ×œ×—×–×•×Ø ×œ×˜×Ø×™×§ של ה coalesce וה fold.

ToCode
1 420
×Ŗ×™×§×•× ×™× קלים, ×Ŗ×™×§×•× ×™× קשים ובדיקות ×‘×¤×Ø×•×™×§×˜ Node.JS שעבדתי עליו ×ž×¦××Ŗ×™ את השורה הזאת:
const [host, port] = address.split(':');
והמשימה ×”×™×Ŗ×” להוהיף ×¤×•×Ø×˜ ברירת מחדל, כך שאם הכתובת לא ×ž×”×Ŗ×™×™×ž×Ŗ ×‘× ×§×•×“×•×Ŗ×™×™× ×•×ž×”×¤×Ø × ×©×Ŗ×ž×© בברירת המחדל 8080. ×”×Ŗ×™×§×•×Ÿ הקל הוא בהך הכל:
const [host, port = '8080'] = address.split(':');
×”×Ŗ×™×§×•×Ÿ הקשה היה ×œ×”×Ŗ×ž×•×“×“ עם כל שאר ×”×“×‘×Ø×™× ×©×ž×©×Ŗ×ž×©×™× יכלו להכניה ×‘×Ŗ×•×š address, למשל להבין ×©×ž×©×Ŗ×ž×©×™× יכולים ×œ×›×Ŗ×•×‘ http ×‘×”×Ŗ×—×œ×”, להוהיף נתיב, ×œ×˜×¢×•×Ŗ בשם של ה host, ×œ×›×Ŗ×•×‘ כתובת IP לא ×Ŗ×§×™× ×” או מיליון ×˜×¢×•×™×•×Ŗ אחרות בהגנון. קוד טוב ×¦×Ø×™×š ×œ×”×¤×Ø×™×“ בין המנגנון שמפענח את הקלט למנגנון שעושה משהו עם הקלט המפוענח, ×•×œ××¤×©×Ø לכל אחד משני המנגנונים לדווח שגיאות בצורה ×©×Ŗ×Ŗ××™× הכי טוב ×œ×ž×©×Ŗ×ž×©. שינוי כזה הוא קשה כי השורה ×”×™×Ŗ×” קבורה בפונקציה של עשרות שורות והמנגנונים היו ×ž×¢×•×Ø×‘×‘×™× יחד. כשיש הט בדיקות טוב אנחנו ×ž×Ø×’×™×©×™× בנוח לבצע גם ×Ŗ×™×§×•× ×™× קשים, אפילו כאלה ×©×“×•×Ø×©×™× ×Ø×™×¤×§×˜×•×Ø×™× ×’ ×ž×©×ž×¢×•×Ŗ×™. ×œ×¦×¢×Ø× ×• ברוב ×”×ž×¢×Ø×›×•×Ŗ כשהמנגנונים השונים בקוד ×ž×¢×•×Ø×‘×‘×™× יחד, זה בא יחד עם זה ×©×œ×ž×¢×Ø×›×Ŗ אין בדיקות ויש המון ×ž×§×Ø×™ קצה לא ×ž×Ŗ×•×¢×“×™×, כך ×©×”×Ŗ×™×§×•×Ÿ הקשה נראה כמעט ×‘×œ×Ŗ×™ אפשרי. כן שווה ×œ×–×›×•×Ø ×©×”×Ŗ×™×§×•×Ÿ הקשה לא יהיה ×”×Ŗ×™×§×•×Ÿ הקשה ×”××—×Ø×•×Ÿ בקוד. אם בכל זאת אנחנו מקבלים ×”×–×“×ž× ×•×Ŗ לבצע אותו, כדאי לנצל אותה כדי ×œ×›×Ŗ×•×‘ הט בדיקות עבור ×”×Ŗ×™×§×•×Ÿ הקשה הבא.

ToCode
1 420
טיפ הקאלה: החזרת תוצאה עתידית המנגנון המובנה של Future בהקאלה הוא אומנם פחות ×ž×Ŗ×•×—×›× ×ž×”×¤×Ø×™×•×Ŗ כמו cats effect אבל בהחלט יכול ×œ×”×™×•×Ŗ שימושי בבעיות ×¤×©×•×˜×•×Ŗ, ולא דורש ××Ø×’×•×Ÿ מחדש של קוד התוכנית. נראה איך זה עובד ×“×Ø×š דוגמה פשוטה. הפונקציה הבאה ×ž×™×™×¦×Ø×Ŗ ×•×ž×—×–×™×Ø×” ×Ø×©×™×ž×” של ×ž×”×¤×Ø×™×:
  def createNumbers(): List[Int] =
    (1 to 10).map(_ =>
      Thread.sleep(Random.nextInt(500))
      Random.nextInt(100)).toList
וכמובן יש טוויהט - בשביל ×œ×™×™×¦×Ø ×ž×”×¤×Ø אנחנו מחכים עד חצי שניה עם Thread.sleep. אם ניקח את ×”×Ø×©×™×ž×” מבחוׄ וננהה להדפיה אותה עם קוד כזה:
createNumbers().foreach(println(_))
הקוד יחכה עד ×©×”×Ø×©×™×ž×” כולה מוכנה ורק אז ידפיה במכה אחת את כל ה-10 ×ž×”×¤×Ø×™×. זה עלול ×œ×”×™×•×Ŗ בעייתי כי אם מאיזושהי היבה התוכנית ×Ŗ×”×Ŗ×™×™× עם שגיאה לפני שהצליחה ×œ×™×™×¦×Ø את כל ×”×Ø×©×™×ž×” אף ×ž×”×¤×Ø לא יודפה. בעזרת מנגנון Future אני יכול ×œ×™×™×¦×Ø ×Ø×©×™×ž×” שתהיה זמינה לקוד החיצוני גם לפני שכל ×”×¤×Ø×™×˜×™× חושבו במלואם. ×”×Ø×¢×™×•×Ÿ בגדול הוא שבמקום ×œ×”×—×–×™×Ø ×Ø×©×™×ž×” של ×ž×”×¤×Ø×™× אני ×ž×—×–×™×Ø ×Ø×©×™×ž×” של תוצאות עתידיות (כן Future מייצג תוצאה עתידית של חישוב). זה הקוד:
  def createFutureNumbers(): List[Future[Int]] =
    (1 to 10).map(_ => Future[Int] {
      Thread.sleep(Random.nextInt(500))
      Random.nextInt(100)
    }).toList
בקוד החיצוני אני לוקח כל Future ו"מדביק" לו קוד שיבוצע אחרי שאותו חישוב עתידי יהיה מוכן. ×‘×“×•×’×ž×Ŗ ההדפהה זה יהיה:
val futures = createFutureNumbers()
futures.foreach(f => f.onComplete(i => println(i.get)))
×œ××—×Ø מכן אני רוצה ×œ×—×›×•×Ŗ עד שכל החישובים ×”×¢×Ŗ×™×“×™×™× ×™×”×Ŗ×™×™×ž×•, לכן אני הופך את ×Ø×©×™×ž×Ŗ החישובים ×”×¢×Ŗ×™×“×™×™× ל Future אחד שיהיה מוכן כשכל ×”×¤×Ø×™×˜×™× ×‘×Ø×©×™×ž×” חושבו בעזרת הפונקציה sequence. פונקציה נוהפת בשם Await.ready ×Ŗ×—×›×” עד ש Future ×ž×”×Ŗ×™×™× וכך תאפשר ×œ×Ŗ×•×›× ×™×Ŗ ×œ×Ø×•×„ עד להיום כל ההדפהות. הך הכל הקוד החיצוני יהיה:
  @main
  def f(): Unit =
      val futures = createFutureNumbers()
      futures.foreach(f => f.onComplete(i => println(i.get)))
      Await.ready(Future.sequence(futures), Duration.Inf)
יותר מזה, ה Future בהקאלה כולל פונקציה בשם flatMap ×©×ž××¤×©×Ø×Ŗ להוהיף לו המשך חישוב. בחזרה לדוגמה נניח שאחרי ×©×”×ž×”×¤×Ø מוכן התוכנית ×Ŗ×—×›×” עוד ×ž×”×¤×Ø מילי שניות לפי ×”×ž×”×¤×Ø שחושב ואז ×Ŗ×¢×œ×” אותו בריבוע, ובהוף תדפיה את ריבועי כל ×”×ž×”×¤×Ø×™×. הקוד המעודכן לזה יהיה בהך הכל:
  def f(): Unit =
      val futures = createFutureNumbers().map {f =>
        f.flatMap(i => Future({
          Thread.sleep(i)
          i * i
        }))
      }

      futures.foreach(f => f.onComplete(i => println(i.get)))
      Await.ready(Future.sequence(futures), Duration.Inf)

ToCode
1 420
לא יכול ללמד את זה שני המימושים הבאים ×‘×¤×™×™×Ŗ×•×Ÿ לזיהוי האם ×ž×”×¤×Ø הוא ראשוני שקולים ×œ×’×ž×Ø×™:
* v1 - *
def is_prime_v1(n):
    for i in range(2, int(n ** 0.5) + 1):
        if n % i == 0:
            return False
    return True
    
* v2 - *
from math import sqrt
def is_prime_v2(n):
    return not any(n % i == 0 for i in range(2, int(sqrt(n)) + 1))
ההבדל היחיד הוא כמה מושגים ×¦×Ø×™×š ×œ×”×›×™×Ø כדי להבין את הקוד. במימוש ×”×Ø××©×•×Ÿ מהפיק להבין מה זה פונקציה ×•×œ×”×Ŗ×ž×•×“×“ עם ×œ×•×œ××•×Ŗ for ו range. המימוש השני כבר דורש היכרות עם Generator Comprehension, עם הפונקציה any ועם פקודת import. מי מהשניים קריא יותר? קשה להגיד. מה שבטוח הוא שחשוב ×œ×”×Ŗ××™× את ×”×Ø×ž×” ×”×ž×•×©×’×™×Ŗ של הקוד ×œ×ž×¢×Ø×›×Ŗ ולהביבה בה הוא נכתב. ב Code Review הערות כמו "הקוד ×ž×”×•×Ø×‘×œ מדי" או "הקוד לא קריא" ××•×ž×Ø×•×Ŗ הרבה פעמים "הקוד ×ž×©×Ŗ×ž×© בפחות מדי מושגים", ולצד השני, הערות כמו "הקוד ×ž×Ŗ×•×—×›× מדי" או "הקוד קשה ×œ×Ŗ×—×–×•×§×”" בהך הכל ××•×ž×Ø×•×Ŗ "הקוד ×ž×©×Ŗ×ž×© ביותר מדי מושגים מהשפה". משחקים עם המדד בקוד שאנחנו ×›×•×Ŗ×‘×™× יכולים ללמד אותנו הרבה על השפה והקוד עצמו.

ToCode
1 420
לא יכול ללמד את זה שני המימושים הבאים ×‘×¤×™×™×Ŗ×•×Ÿ לזיהוי האם ×ž×”×¤×Ø הוא ראשוני שקולים ×œ×’×ž×Ø×™:
* v1 - *
def is_prime_v1(n):
    for i in range(2, int(n ** 0.5) + 1):
        if n % i == 0:
            return False
    return True
    
* v2 - *
from math import sqrt
def is_prime_v2(n):
    return not any(n % i == 0 for i in range(2, int(sqrt(n)) + 1))
ההבדל היחיד הוא כמה מושגים ×¦×Ø×™×š ×œ×”×›×™×Ø כדי להבין את הקוד. במימוש ×”×Ø××©×•×Ÿ מהפיק להבין מה זה פונקציה ×•×œ×”×Ŗ×ž×•×“×“ עם ×œ×•×œ××•×Ŗ for ו range. המימוש השני כבר דורש היכרות עם Generator Comprehension, עם הפונקציה any ועם פקודת import. מי מהשניים קריא יותר? קשה להגיד. מה שבטוח הוא שחשוב ×œ×”×Ŗ××™× את ×”×Ø×ž×” ×”×ž×•×©×’×™×Ŗ של הקוד ×œ×ž×¢×Ø×›×Ŗ ולהביבה בה הוא נכתב. ב Code Review הערות כמו "הקוד ×ž×”×•×Ø×‘×œ מדי" או "הקוד לא קריא" ××•×ž×Ø×•×Ŗ הרבה פעמים "הקוד ×ž×©×Ŗ×ž×© בפחות מדי מושגים", ולצד השני, הערות כמו "הקוד ×ž×Ŗ×•×—×›× מדי" או "הקוד קשה ×œ×Ŗ×—×–×•×§×”" בהך הכל ××•×ž×Ø×•×Ŗ "הקוד ×ž×©×Ŗ×ž×© ביותר מדי מושגים מהשפה". משחקים עם המדד בקוד שאנחנו ×›×•×Ŗ×‘×™× יכולים ללמד אותנו הרבה על השפה והקוד עצמו.

ToCode
1 420
×ž×›×Ŗ×‘ פתוח ×œ×Ŗ×œ×ž×™×“ ×©× ×Ø×“× (או: ואם אני ×¦×Ø×™×š עוד זמן) יש שני הוגים של ×©×™×¢×•×Ø×™× משעממים - לפעמים זה משעמם כי אנחנו ×ž×›×™×Ø×™× את כל ×”×—×•×ž×Ø, ולפעמים כי הקצב לא ×ž×Ŗ××™× ואנחנו פשוט לא מבינים כלום. השיעמום מההוג ×”×Ø××©×•×Ÿ יותר קל. כשאני יודע מה קורה אני יכול לנווט בשלווה ×œ×—×“×©×•×Ŗ, לפייהבוק, ×œ×˜×•×•×™×˜×Ø או לכל גנב זמן אחר. פעם בכמה דקות אפשר להוציא את ×”×©× ×•×Ø×§×œ כדי ×œ×Ø××•×Ŗ ×©×”×—×•×ž×Ø עדיין ×ž×•×›×Ø. השיעמום היותר קשה הוא זה מההוג השני. שיעמום של חוהר הבנה הוא ×ž×Ŗ×™×©, כי אנחנו משקיעים המון אנרגיה ×‘×œ× ×”×•×Ŗ להבין, ×‘×œ× ×”×•×Ŗ להדביק את הקצב. במבנה של הרצאה גם מאוד קשה ×œ×Ø××•×Ŗ את הפער. זה השיעמום ×”×ž×Ø×“×™×. ×›×©× ×Ŗ×§×œ×™× בשיעמום כזה האינהטינקט שלנו מטעה. באותו רגע אנחנו רק מחפשים "×œ×”×’×•×Ø את הפער", ×œ× ×”×•×Ŗ "להבין כמה שאפשר", להמשיך להקשיב אפילו שזה משעמם כי למדנו בבית הפר שיש פה משהו חשוב ×•×©×”×”×–×“×ž× ×•×Ŗ הזאת לא תחזור. עכשיו מקשיבים, אחרי זה מבינים. ×•×ž×Ø×•×‘ ×Ø×¦×•×Ÿ להבין העיניים × ×¢×¦×ž×•×Ŗ והפה מפהק, כי הגוף כבר יודע מה שהמוח לא רוצה לשמוע. ×•×”××ž×Ŗ הכואבת היא שהפער יותר גדול ממה שהמוח היה רוצה לדמיין. זה לא איזה "×Ø×¢×™×•×Ÿ" קטן ×©×¦×Ø×™×š לשמוע והכל יהתדר, אלא עבודת השלמה ×ž×©×ž×¢×•×Ŗ×™×Ŗ כדי ×œ×”×’×•×Ø את אותו פער. גם אם בקורה נתנו לנושא חצי שעה, אולי אני ×¦×Ø×™×š ארבע שעות כדי להבין את הנושא הזה. הקצב שלי יכול ×œ×”×™×•×Ŗ שונה מהקצב של ××—×Ø×™× והניהיון ללמוד בקצב שלא ×ž×Ŗ××™× לי הוא פשוט ×ž×Ŗ×™×©. במצב כזה ×”×¤×™×Ŗ×Ø×•×Ÿ הכי טוב והיחיד שעובד הוא להיצמד לקצב שלך, גם אם זה לא הקצב של ×”×›×™×Ŗ×” או הוידאו. זה בהדר ×œ×Ø××•×Ŗ את אותו שיעור כמה פעמים. זה בהדר להשקיע שבועיים בדף ×Ŗ×Ø×’×™×œ×™×, גם אם מישהו אחר פתר אותו ביום. לימוד זה לא תחרות והקבוצה לא חכמה יותר מכל אחד מהאנשים בה. הקצב היחיד שעובד הוא הקצב שעובד ×¢×‘×•×Ø×š.

ToCode
1 420
טיפ הקאלה: הדפהת דיבג באמצע Pipeline בשפות ×¤×•× ×§×¦×™×•× ××œ×™×•×Ŗ ×“×™× ×ž×™×•×Ŗ כמו קלוז'ר או ××œ×™×§×”×™×Ø, יש ××•×¤×Ø×˜×•×Ø Pipeline ששולח את התוצאה של פונקציה אחת בתור קלט לפונקציה הבאה בתור. לדוגמה הקוד הבא בקלוז'ר:
(->> 10
  (range)
  (map #(* %1 %1))
  (reduce +)
  (println))
לוקח את ×”×ž×”×¤×Ø 10, שולח אותו בתור ×”×¤×Ø×ž×˜×Ø לפונקציה range, אחרי זה לוקח את התוצאה של range בתור קלט ×œ×¤×•× ×§×¦×™×™×Ŗ ה map, את התוצאה של map הוא שולח ל reduce ואת זה ישלח ל println כדי להדפיה. הך הכל נקבל על המהך את הכום ריבועי ×”×ž×”×¤×Ø×™× מאפה עד תשע. אם יש בעיה בחישוב אפשר ×œ×”×’×“×™×Ø ×‘×§×œ×•×Ŗ פונקציה שמדפיהה את ×”×¤×Ø×ž×˜×Ø×™× שקיבלה ×•×ž×—×–×™×Ø×” ××•×Ŗ×, ואז להוהיף אותה באמצע ה Pipeline כדי ×œ×Ø××•×Ŗ הטטוה ביניים:
(defn debug [n]
  (println n)
  n)

(->> 10
  (debug)
  (range)
  (debug)
  (map #(* %1 %1))
  (debug)
  (reduce +)
  (println))
הדפהות הביניים עוזרות להבין מה קורה שם ×•×œ×Ŗ×§×Ÿ בעיות ×›×©×¦×Ø×™×š. ×‘×ž×¢×‘×Ø להקאלה מאוד ×”×Ŗ×’×¢×’×¢×Ŗ×™ למנגנון הזה, כיוון שבהקאלה קריאה בשרשרת ×œ×¤×•× ×§×¦×™×•×Ŗ ×ž×©×Ŗ×ž×©×Ŗ בתחביר ×”×ž×Ŗ×•×“×•×Ŗ של תכנות מונחה עצמים. ככה נראה קוד הקאלה מקביל לקלוז'ר ×ž×Ŗ×—×™×œ×Ŗ הפוהט:
  println(
    (0 to 9)
      .map(_.pow(2))
      .sum
  )
אז זה היה פשוט בהקאלה אני לא יכול ×œ×”×©×Ŗ×ž×© פעמיים ×‘×¤×Ø×ž×˜×Ø של פונקציה ×× ×•× ×™×ž×™×Ŗ אבל במקום זה יש לי פונקציה ×œ×”×¢×œ×•×Ŗ בריבוע, טווח כולל את ×”×ž×”×¤×Ø ×”××—×Ø×•×Ÿ לכן מהיימים בתשע ויש פונקציית sum אז לא ×¦×Ø×™×›×™× את reduce. ובכל זאת שני ×“×‘×Ø×™× פוגעים פה בחוויה - ×”×Ø××©×•×Ÿ הוא ש println ×¦×Ø×™×š ×œ×”×™×›×Ŗ×‘ ×‘×”×Ŗ×—×œ×” בתור פונקציה, והשני שגם אם אכתוב פונקציית debug כמו שהיתה לי בקלוז'ר היא לא חלק מהפייפליין. ×œ×©×ž×—×Ŗ× ×• מנגנון ה Extensions של הקאלה ×ž××¤×©×Ø ×œ×¤×Ŗ×•×Ø את שתי הבעיות ×™×—×”×™×Ŗ ×‘×§×œ×•×Ŗ. אני ×ž×’×“×™×Ø Extension גנרי שיוהיף את הפונקציה debug לכל ערך בשפה. זה נראה ככה:
extension [A](value: A)
  def debug(): A =
    println(value)
    value
}

@main
def main(): Unit =
  println(
    (0 to 9)
      .debug()
      .map(_.pow(2))
      .debug()
      .sum
  )
והפלט:
NumericRange 0 to 9
Vector(0, 1, 4, 9, 16, 25, 36, 49, 64, 81)
285
יותר מזה, בגלל שחלק מהאוביקטים בשפה מחושבים בצורה עצלה (למשל Range), אפשר להוהיף עוד פונקציה ×©×Ŗ×”×¤×•×š ××™×˜×Ø×˜×•×Ø×™× ×œ×Ø×©×™×ž×•×Ŗ ואז תדפיה ××•×Ŗ×, רק שימו לב לא להפעיל אותה על ×“×‘×Ø×™× אינהופיים:

extension [A](value: A)
  def debug(): A =
    println(value)
    value

  def eagerDebug(): A = value match {
    case it: Iterable[_] =>
      println(it.toList)
      value
    case it: Iterator[_] =>
      println(it.toList)
      value
    case _ =>
      println(value.getClass)
      value
  }

@main
def main(): Unit =
  println(
    (0 to 9)
      .eagerDebug()
      .map(_.pow(2))
      .debug()
      .sum
  )
ואם כבר הוהפנו את debug נוכל ×œ×”×©×Ŗ×ž×© בה גם בהוף הפייפליין כדי להדפיה בהגנון קלוז'ר:
@main
def main(): Unit =
  (0 to 9)
    .eagerDebug()
    .map(_.pow(2))
    .debug()
    .sum
    .debug()

ToCode
1 420
×”×§×Ø×™×¤×˜×™× ב JavaScript? תנו צ'אנה ×œ×“×™× ×•×–××•×Ø אחת הבעיות בשימוש ב node.js ×œ×›×Ŗ×™×‘×Ŗ ×”×§×Ø×™×¤×˜×™× היא הקובׄ package.json, או יותר נכון ×”×¦×•×Ø×š לצרף אותו ×œ×”×§×Ø×™×¤×˜ כדי ×œ×”×Ŗ×§×™×Ÿ את ×”×Ŗ×œ×•×™×•×Ŗ. התוכנית הבאה ב node.js ×ž×Ŗ×•×š קוד הדוגמה של המודול wikipedia על npm לא יכולה ×œ×Ø×•×„ לבד:
const wiki = require('wikipedia');

(async () => {
 try {
  const page = await wiki.page('Batman');
  console.log(page);
  //Response of type @Page object
  const summary = await page.summary();
  console.log(summary);
  //Response of type @wikiSummary - contains the intro and the main image
 } catch (error) {
  console.log(error);
  //=> Typeof wikiError
 }
})();
בשביל שהיא תעבוד אני ×¦×Ø×™×š ×œ×™×¦×•×Ø איתה בתיקיה קובׄ package.json ולהפעיל npm install או להפעיל
npm install wikipedia
×ž×©×•×Ø×Ŗ הפקודה לפני הרצה. עכשיו בואו נראה איך אותו ×˜×Ø×™×§ יעבוד עם דינו. דבר ×Ø××©×•×Ÿ ×¦×Ø×™×š ×œ×©× ×•×Ŗ את שורת ה require ל import, ולציין שאני טוען את המודול מ npm כך שקוד התוכנית יהיה:
import wiki from 'npm:wikipedia';


(async () => {
 try {
  const page = await wiki.page('Batman');
  console.log(page);
  //Response of type @Page object
  const summary = await page.summary();
  console.log(summary);
  //Response of type @wikiSummary - contains the intro and the main image
 } catch (error) {
  console.log(error);
  //=> Typeof wikiError
 }
})();
ומכאן זה רק ×ž×©×Ŗ×¤×Ø. אני ×ž×Ø×™×„ את הקוד ודינו ×ž×Ŗ×—×™×œ ×œ×”×–×”×™×Ø אותי:
$ deno run demo.js

āš ļø  ā”Œ Deno requests env access to "npm_config_no_proxy".
   ā”œ Run again with --allow-env to bypass this prompt.
   ā”” Allow? [y/n] (y = yes, allow; n = no, deny) >
שים לב, הוא ××•×ž×Ø, ×”×”×§×Ø×™×¤×˜ מעוניין ×œ×§×Ø×•× מידע ×ž×ž×©×Ŗ× ×” הביבה ואפילו כותב את השם של ×ž×©×Ŗ× ×” ההביבה. אני יכול לאשר עם y או ×œ×”×Ø×™×„ מחדש עם --allow-end כדי שלא ישאל יותר על גישה ×œ×ž×©×Ŗ× ×™ הביבה. אני ×ž××©×Ø וממשיכים ×œ×©××œ×•×Ŗ הבאות. כך זה נראה בהוף:
$ deno run demo.js

āœ… Granted env access to "npm_config_no_proxy".
āœ… Granted env access to "NPM_CONFIG_NO_PROXY".
āœ… Granted env access to "no_proxy".
āœ… Granted env access to "NO_PROXY".
āœ… Granted env access to "npm_config_https_proxy".
āœ… Granted env access to "NPM_CONFIG_HTTPS_PROXY".
āœ… Granted env access to "https_proxy".
āœ… Granted env access to "HTTPS_PROXY".
āœ… Granted env access to "npm_config_proxy".
āœ… Granted env access to "NPM_CONFIG_PROXY".
āœ… Granted env access to "all_proxy".
āœ… Granted env access to "ALL_PROXY".
āœ… Granted read access to "/Users/ynonp/Library/Caches/deno/npm/node_modules".
āœ… Granted read access to "/Users/ynonp/Library/Caches/deno/node_modules".
āœ… Granted read access to "/Users/ynonp/Library/Caches/node_modules".
āœ… Granted read access to "/Users/ynonp/Library/node_modules".
āœ… Granted read access to "/Users/ynonp/node_modules".
āœ… Granted read access to "/Users/node_modules".
āœ… Granted read access to "/node_modules".
āœ… Granted net access to "en.wikipedia.org".
ואחרי זה מגיע הפלט ×”×Ø×’×™×œ של ×”×”×§×Ø×™×¤×˜. אם אני הומך על ×”×”×§×Ø×™×¤×˜ אני יכול פעם הבאה ×œ×”×Ø×™×„ אותו עם ×”×ž×Ŗ×’×™×:
$ deno run --allow-net --allow-read --allow-env demo.js
כדי לדלג על ×”×©××œ×•×Ŗ. הך הכל כתיבה והרצת ×”×§×Ø×™×¤×˜ עם דינו × ×Ŗ× ×” לי שני יתרונות ×ž×©×ž×¢×•×Ŗ×™×™× על פני node: 1. אין ×¦×•×Ø×š בקובׄ package.json, כל ×”×Ŗ×œ×•×™×•×Ŗ ×Ø×©×•×ž×•×Ŗ ×‘×Ŗ×•×š קובׄ ×”×”×§×Ø×™×¤×˜ וההתקנה קורית ××•×˜×•×ž×˜×™×Ŗ בהרצה הראשונה. 2. אני חושש הרבה פחות מקוד זדוני, כי בברירת המחדל ×”×”×§×Ø×™×¤×˜ רׄ עם מעט מאוד הרשאות.

ToCode
1 420
היום ×œ×ž×“×Ŗ×™ - לא כזה פשוט לחפש מההוף בביטוי ×Ø×’×•×œ××Ø×™ ×”×Ŗ×—×™×œ היום Advent Of Code ואומנם אני כנראה לא אצליח ×œ×¤×Ŗ×•×Ø את כל החידות שלו השנה על היום ×”×Ø××©×•×Ÿ אי אפשר ×œ×•×•×Ŗ×Ø. בחידה ×”×™×•×ž×™×Ŗ היה ×¦×Ø×™×š למצוא ×“×‘×Ø×™× ×‘×Ŗ×•×š טקהטים. לא כזה מהובך עד ×©× ×Ŗ×§×œ×™× ×‘×˜×Ø×™×§ הקטן שמקלקל הכל. ×”×Ŗ×Ø×’×™×œ ×•×”×˜×Ø×™×§ ההיפור בגדול היה למצוא הפרות ×‘×Ŗ×•×š שורת טקהט, כשההפרות ×™×›×•×œ×•×Ŗ להופיע בתור הפרות ממש כמו 4, 9 או 2 או בתור שם ההפרה כמו four, nine או two. אנחנו ×¦×Ø×™×›×™× למצוא ×‘×”×™× ×Ŗ×Ÿ שורת טקהט את ההיפרה הראשונה וההיפרה האחרונה בה. למשל בשורה:
24
ההיפרה הראשונה היא 2 והאחרונה היא 4. בשורה:
abc7d
ההיפרה 7 היא גם ההיפרה הראשונה וגם האחרונה. ובשורה:
eightwo
ההיפרה 8 היא הראשונה ו 2 היא האחרונה. ניהוי 1 - חיפוש כל המופעים ×Ø×¢×™×•×Ÿ ×Ø××©×•×Ÿ שאפשר ×œ× ×”×•×Ŗ כדי למצוא את ההיפרה הראשונה והאחרונה הוא חיפוש גלובאלי, ×‘×¤×™×™×Ŗ×•×Ÿ זה יהיה:
>>> digits = [str(i) for i in range(10)] + ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine"]

>>> digits_r = '|'.join(digits)

>>> re.findall(digits_r, "1abc3")
['1', '3']
וזה עובד עד שזה מפהיק לעבוד:
>>> re.findall(digitsr, "eightwo")
['eight']
כי הבעיה שה t ×ž×©×•×Ŗ×£ גם ל eight וגם ל two ומנוע הביטויים ×”×Ø×’×•×œ××Ø×™×™× כבר איבד את ה t כשהוא מצא את eight. ×¤×™×Ŗ×Ø×•×Ÿ - חיפוש הפוך בהיעדר ×“×Ø×š טובה לחפש מההוף בביטוי ×Ø×’×•×œ××Ø×™ נדרשת יצירתיות - למשל להפוך את כל הטקהטים לפני החיפוש ואז להפוך חזרה אחרי שמוצאים כדי ×œ×“×¢×Ŗ איזו היפרה מצאנו. ×‘×¤×™×™×Ŗ×•×Ÿ זה קל:
>>> digits_re = '|'.join([d[::-1] for d in digits])
>>> [r[::-1] for r in re.findall(digits_re, "eightwo"[::-1])]
['two']