Summing numbers with Iterators in Rust | Advent of Code 2022 Day 1

Sdílet
Vložit
  • čas přidán 30. 11. 2022
  • The code for day 1: github.com/ChristopherBiscard...
  • Věda a technologie

Komentáře • 60

  • @aranasaurus
    @aranasaurus Před rokem +38

    I loved this! I’m doing AoC in rust this year to learn/get better with rust. So being able to watch your process for solving a problem I just solved myself, in detail like this is a huge help! I was able to pick up on a bunch of rust idioms just by you talking through your approach to the problem and considering your options, so I really hope you keep doing these, and keep the detailed talking through the problem in!

    • @markday3145
      @markday3145 Před rokem +1

      I, too, really like seeing how other people approach a problem. I almost always learn something new. And I really appreciate Chris talking through the problem and the solution rather than just presenting a solution (it's often that talking through it part where I learn something).

    • @Zzznmop
      @Zzznmop Před rokem +1

      Plus one to this! as a noob to rust, I can get a basic program written jogging my C++ memories, but it is hard to know how “good” that solution is without exploring all the tools available in rust :)

    • @dmhowcroft
      @dmhowcroft Před rokem +3

      Yeah, same--I'm coming from Python and don't often use map-apply programming patterns, so seeing how a Rust programmer solves these problems after I've done it in my hacky way is great for learning the style differences between the languages :)

  • @squelchedotter
    @squelchedotter Před rokem +36

    The reason you can't sort an iterator is because it wouldn't have anywhere to store it. An iterator only has access to the current value, not past or future ones.
    Thus sorting requires collecting all of the elements into some sort of new container, which can then be sorted in place. This is what itertools does.

    • @chrisbiscardi
      @chrisbiscardi  Před rokem +10

      If that's what itertools does then there's no technical restriction to it being implemented. Yes, you can't sort an Iterator in-place because it requires all of the values, but terminating functions like collect have to create a new Vec anyway. The Iterator trait itself doesn't preclude this behavior from existing in the stdlib.
      Also, I was able to find the reason after the recording! Basically nobody seems to have RFC'd the functionality into the stdlib: github.com/rust-lang/rust/issues/38882

    • @icelk
      @icelk Před rokem +9

      @@chrisbiscardi I think the reason for a sort method on iterators not being implemented is to avoid confusion about allocation. Iterators are also lazy, and sorting them completely defeats that. The trait is also defined in `core::iter::Iterator`, where an allocator isn't available.

    • @rutabega306
      @rutabega306 Před rokem +2

      Doesn't seem so hard to collect into a Vec first then sort. And that makes it clear what the memory implications are!

    • @mzg147
      @mzg147 Před rokem

      But what about making an index array? The Sort iterator would have a [u32] array that specifies the sorted order, e.g. for vec![2,3,1] it would be [2,0,1]
      Or with another approach, each next invocation for the iterator would find the n-th lowest element. That would be less optimal though, but didn't require memory

    • @yaksher
      @yaksher Před 7 měsíci

      @@mzg147 A generic iterator can't be traversed multiple times; it might mutate or consume state, for example.

  • @markday3145
    @markday3145 Před rokem +6

    I ended up solving this one almost the same way you did, and then experimented with other ideas just for the sake of learning. A few observations:
    There are also sort_unstable, sort_unstable_by, etc. They can be more efficient if it is OK to rearrange values that compare equal. On a problem of this size, the performance difference would be tiny; but it's good to know about for those cases when you have much larger inputs.
    For parsing the numbers, you can do:
    .map(str::parse::)
    (with no closure). I find that just a little easier to read.
    To sort in reverse, you can do:
    .sort_by(|a, b| a.cmp(b).reverse())
    I think that more obviously conveys that you want it sorted in reverse. It's easy to overlook that `a` and `b` were swapped in the closure. There is also sort_by_key() combined with std::cmp::Reverse, which I find a little harder to read (and remember).
    I really hope you will post more Advent of Code videos. I enjoy seeing how other people approach problems, including going back and refining a solution.

  • @devbites77
    @devbites77 Před rokem

    Thoroughly explained, and thoroughly enjoying learning rust.

  • @michaelalls8230
    @michaelalls8230 Před rokem

    As someone trying to use AOC to learn Rust this is great! Learning so much! Thank You!

  • @drewmiddleton9715
    @drewmiddleton9715 Před rokem +1

    this is awesome thank you. I just tackled problem 1 in Python and am starting to learn Rust, so will be following along with this series to try and learn :)

  • @tobiasgraf5246
    @tobiasgraf5246 Před rokem

    Thx for sharing your solution.
    I'm currently learning rust and use advent of code to play around with the language, and it's very helpful to have you explain certain aspects.
    Tomorrow, I will have to change my repository to your setup. That looks way more reasonable than mine 😅

  • @gergelypaless5042
    @gergelypaless5042 Před rokem +1

    Looking forward to this! I am also planning to do this myself. Interested in your solutions😁 Keep it up!

  • @opposite342
    @opposite342 Před rokem

    I'm mindblown by the FP usage here

  • @ballingt
    @ballingt Před rokem

    Everything here is helpful, but gosh thanks for the project setup notes! Exactly what will be helpful when I get around to day 3 tonight.

  • @inzyster
    @inzyster Před rokem

    I've been meaning to start learning Rust. I mainly work with C#, so this will be very useful!

  • @laupetre
    @laupetre Před rokem

    Great video! Looking forward to learning rust with this version of AoC

  • @BlackSharkfr
    @BlackSharkfr Před rokem +4

    Since the sums are stored in a Vector, I would have sorted normally and then used iter().rev().take(3)

    • @markday3145
      @markday3145 Před rokem

      Good suggestion. I ended up sorting, then calling .reverse(), then .iter().take(3).sum(). I forgot about reverse iterators.
      The .rev() method to turn a forward iterator into a reverse iterator just feels weird to me, an so I tend to forget about it. I would expect something like .iter_rev() or similar to directly construct a reverse iterator. (I get why they did it that way. That one method can be applied to iterators producing owned values, shared references, or exclusive references.)

  • @adityasanthosh702
    @adityasanthosh702 Před rokem

    If anyone is doing this on windows, you will encounter an error while you are splitting because in Windows, the line seperators are
    instead of

  • @jasperdevries1726
    @jasperdevries1726 Před rokem

    Like you said, parse returns a result but max returns an Option. While you want to unwrap them both in this case, they are quite different.

  • @erifetim
    @erifetim Před rokem

    Nice, love comparing solutions and not just blatantly copying yours because I'm stuck!
    I was thinking if it was possible to extend the Iterator trait such that you could pick the "top_3()" items or something. Digging through the Iterator file, it was a little too intimidating to start anything

    • @chrisbiscardi
      @chrisbiscardi  Před rokem +1

      you could start with using scan, using a 3 element sorted array as the state and then write an extension trait that uses scan behind the scenes doc.rust-lang.org/std/iter/trait.Iterator.html#method.scan

  • @magnusmarkling
    @magnusmarkling Před rokem

    How would you "group the values using itertools"? Would love to see that!

    • @chrisbiscardi
      @chrisbiscardi  Před rokem

      I probably would've ended up using coalesce like this: play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=a8f94ea0b82ce025dfe198aab4ee4784
      The coalesce uses the outer Result to do the iteration, while the inner Result is the result from our parse. Otherwise the answer is mostly the same docs.rs/itertools/latest/itertools/trait.Itertools.html#method.coalesce

  • @benibachmann9274
    @benibachmann9274 Před rokem +1

    Also trying to do this year‘s AoC with Rust again! My goal is to reach the top 100 once. But it’s not looking very realistic after day 1. 😂

  • @phenanrithe
    @phenanrithe Před rokem +1

    Funny challenge, though the questions are released in the middle of the night so I'm not even paying attention to the indicated delay to solve them. The delay should count from the instant the question is shown. +1 point for the TDD approach!
    I used itertools and coalesce so that I don't have to load the whole file in memory. I know it's not that big, it's just for the sake of trying.

    • @erifetim
      @erifetim Před rokem +1

      Probably wouldn't work for global ranking, but for personal scores I'd absolutely love to have a "time since opening the challenge".

    • @phenanrithe
      @phenanrithe Před rokem

      @@erifetim True, that wouldn't work for global ranking.

  • @jomy10-games
    @jomy10-games Před rokem

    I’m writing every puzzle in a different language this year. I did the first day in shell script

  • @mustafazakiassagaf1757

    hey i don't get why sum need a type, won't rust know the type from parse?

  • @iuiz
    @iuiz Před rokem

    This video is missing how you developed the code step by step :(.
    The whole using closures part is elegant, when it is ready, but when I want to add a simple println in the closure without the rest of them, I get errors, because I need to assign the typed correctly. This feels like a "write everything at once" solution and not something you can develop organically. I went for two for loops instead, which looks awful, but as I am new to Rust i could develop it step by step.
    However thank you for your video and the provided insights.

    • @chrisbiscardi
      @chrisbiscardi  Před rokem

      How are you adding the println? and what do you mean by "without the rest of them"? Do you mean that you want to have a println at an arbitrary location in the chain? If so there's a function for that: inspect. Here's a link to the documentation for it: doc.rust-lang.org/std/iter/trait.Iterator.html#method.inspect and I would also suggest using the dbg! macro instead of println! for debugging purposes.
      Personally, my step-by-step *was* write the code from top to bottom so I edited out the typing and covered the result. I wasn't doing any sort of debugging or whatnot. What sort of additional explanation are you looking for? Is it the existence of the iterator functions and where to find them?

    • @iuiz
      @iuiz Před rokem

      @@chrisbiscardi Thank you very much for your time to answer. I dive deeper into Rust every day and it gets a bit clearer. I cannot answer anymore, what exactly I did wrong, but ownership issues after the collect is something I keep running into. I could do the day 3 solution kinda slow but it worked out thanks to your tutorials :).
      dbg! seems to have some problems with claiming ownership and destroys code I had after it, so I switched back to println and the real debugger. I might skip a day to do some more basic Rust tutorials.
      Thanks again for all your videos.

  • @krige
    @krige Před rokem

    1:45 Why did choose to use --lib?

    • @chrisbiscardi
      @chrisbiscardi  Před rokem

      I chose --lib because I knew I was going to set up my binaries in the bin folder, so instead of generating a main.rs, --lib generates a lib.rs with a test that my bin folder can use

  • @kewang7473
    @kewang7473 Před rokem

    Why not binaryheap

  • @gnu-linux2728
    @gnu-linux2728 Před rokem

    Dude I tray to get the code with git clone ........ but maybe something ist wrong with your repo

  • @petarvujovic
    @petarvujovic Před rokem

    I solved this challenge in almost the exact same way about an hour ago. Love to see people thinking in a similar way to myself

  • @krige
    @krige Před rokem

    1:38 ... you don't need because you are using what?

  • @vladimirbauer6604
    @vladimirbauer6604 Před rokem +2

    If you don't want to panic on bad input value, you can do '.filter_map(|item| item.parse::().ok())' instead which will just skip bad value.

    • @Andrew-jh2bn
      @Andrew-jh2bn Před rokem +5

      In this case I think it would be better to panic or return an error than just silently ignore parsing issues.

  • @chair547
    @chair547 Před rokem

    Collecting and sorting is nlogn and memory inefficient. It should be possible to get the top 3 without collecting or sorting and also being o(n)

  • @CuriousSpy
    @CuriousSpy Před rokem

    You cannot use sort to iter because iter have access to next element like linkedlist, but when you sorting array you need access to an entire array

  • @echobucket
    @echobucket Před rokem

    Wow people actually use nushell. I mean I think it's cool, but I've been using bash/zsh too long.

    • @chrisbiscardi
      @chrisbiscardi  Před rokem

      Nushell has been my primary shell for over a year now. I still have some bash/zsh isms in my fingers but it's been really solid for me and I really enjoy the data features

  • @burnere633
    @burnere633 Před rokem +2

    Hi. Request: could you not provide hints in the title of AoC videos? :)

    • @chrisbiscardi
      @chrisbiscardi  Před rokem +1

      hey! I'm happy to try. Did you feel like today's title was too much of a hint? I felt like the question asked us to sum numbers pretty directly.

    • @burnere633
      @burnere633 Před rokem

      @@chrisbiscardi I don't know, I haven't yet looked at today's problem; so now you've already biased me in some way. :D

    • @carterplasek498
      @carterplasek498 Před rokem +2

      @@chrisbiscardi I was reminded to participate today by your video, and just seeing the parse function in the glance at the thumbnail felt like too much of a hint, especially since all of the code is in the thumbnail, I think the title was subtle enough though

  • @jawad9757
    @jawad9757 Před rokem

    kinda spoiling it by putting the code for the solution in the thumbnail, esp for people who haven't had the chance to do it yet

  • @echoptic775
    @echoptic775 Před rokem +7

    Im impressed how can you talk so much without saying anything and do so much without doing anything

  • @verified_tinker1818
    @verified_tinker1818 Před rokem +5

    You can do Part 2 in one pass and without a heap allocation by using `fold()`:
    ```
    pub fn sum_top_3(input: &str) -> u32 {
    input
    .split("

    ")
    .map(|food_items_per_elf| {
    food_items_per_elf
    .lines()
    .map(|calories_per_item| calories_per_item.parse::().unwrap())
    .sum::()
    })
    .fold([0, 0, 0], |mut top_3, calories_per_elf| {
    // The `unwrap()` could be avoided by writing `if let Some(min)`,
    // but I believe it's better to be explicit. `if let` would fail silently.
    let min = top_3.iter_mut().min().unwrap();
    *min = u32::max(calories_per_elf, *min);
    top_3
    })
    .into_iter()
    .sum()
    }
    ```
    This gives you a time complexity of O(n) and a space complexity of O(1).

  • @QmVuamFtaW4
    @QmVuamFtaW4 Před 8 měsíci

    i came up with something like this 💀
    let file = fs::read_to_string("input.txt").expect("Error reading input file");
    let mut vec: Vec = Vec::new();
    let mut sum = 0;
    let mut largest = 0;
    for i in file.split("

    ") {
    for j in i.split("
    ") {
    sum += match j.parse::() {
    Ok(val) => val,
    Err(..) => 0,
    };
    }
    vec.push(sum);
    if sum > largest {
    largest = sum;
    }
    sum = 0;
    }
    vec.sort_by(|a, b| b.cmp(a));
    for i in 0..=2 {
    sum += vec[i];
    }