7

Tracking Issue for `Iterator::intersperse` · Issue #79524 · rust-lang/rust · Git...

 4 years ago
source link: https://github.com/rust-lang/rust/issues/79524
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Copy link

Task lists! Give feedback

Member

camelid commented on Nov 28, 2020

edited by m-ou-se

This is a tracking issue for the Iterator::intersperse and intersperse_with methods. It is based on itertools::intersperse and is the iterator equivalent of Vec::join.

The feature gate for the issue is #![feature(iter_intersperse)].

Public API

// core::iter

pub trait Iterator {
    fn intersperse(self, separator: Self::Item) -> Intersperse<Self>
    where
        Self: Sized,
        Self::Item: Clone;

    fn intersperse_with<G>(self, separator: G) -> IntersperseWith<Self, G>
    where
        Self: Sized,
        G: FnMut() -> Self::Item;
}

#[derive(Debug, Clone)]
pub struct Intersperse<I: Iterator> where I::Item: Clone {}

impl<I> Iterator for Intersperse<I> where I: Iterator, I::Item: Clone {
    type Item = I::Item;
}

pub struct IntersperseWith<I, G> where I: Iterator {}

impl<I, G> Debug for IntersperseWith<I, G> where I: Iterator + Debug, I::Item: Debug, G: Debug {}

impl<I, G> Clone for IntersperseWith<I, G> where I: Iterator + Clone, I::Item: Clone, G: Clone {}

impl<I, G> Iterator for IntersperseWith<I, G> where I: Iterator, G: FnMut() -> I::Item {
    type Item = I::Item;
}

Steps / History

  • Implement intersperse: #79479
  • Add intersperse_with: #80567
  • Add impl<I: FusedIterator> FusedIterator for Intersperse<I>? (Can be added after stabilization, so not urgent.)
  • Stabilization PR

Unresolved Questions

None.

Copy link

Member

m-ou-se commented on Jan 13

#80567 is adding intersperse_with to this feature.

Copy link

Contributor

cyqsimon commented on Mar 28

edited

I would like to suggest a simpler, alternative name for this method - weave. At the time of comment, the word weave is not yet used in Rust std.

Intersperse is quite verbose (especially when compared to Vec::join), but more importantly rarely used in common English. weave is much more common and concise in comparison.

Copy link

Contributor

ibraheemdev commented on Mar 29

edited

Scala and haskell both use intersperse. Scalaz calls it intercalate...

Copy link

tmplt commented on May 3

I would like to suggest a simpler, alternative name for this method - weave.

weave is not on the list of terms I would search for when looking for the expected behavior. I'd even consider it vanity.

Intersperse is quite verbose (especially when compared to Vec::join), but more importantly rarely used in common English. weave is much more common and concise in comparison.

A verbose function name describes its effect better. More so if the term is uncommon; there is then less ambiguity. intersperse does exactly what is says: intersperse elements with a common separator.

why not use join and join_with for iterators too?

Copy link

fosskers commented on May 18

Scala and haskell both use intersperse. Scalaz calls it intercalate...

intercalate is slightly different.

intersperse :: a -> [a] -> [a] 

intercalate :: [a] -> [[a]] -> [a] 

Copy link

kaylynn234 commented on Jun 20

edited

why not use join and join_with for iterators too?

I feel like join implies the creation of something monolithic from a sequence and a separator, i.e

let items = vec!["foo", "bar", "blah"];
let result = items.join(" ");

will create the string foo bar blah, and I don't have any issue with that.

But in my mind intersperse better communicates the idea of placing separators in between a sequence, without creating something monolithic using the results of that.

I suppose I mean that if I intersperse an iterator of &strs with " ", it makes sense to me that I get an iterator back.
But if I have an iterator of &strs, and the method is named join, I feel like it's a little less clear. Without looking further into it, it might seem like that I'd get a concatenated String back, or similar.
For other types it may be less unclear but I'm still unsure on whether calling the method join is a good fit.

Copy link

arnabanimesh commented on Jun 20

edited

I feel like join implies the creation of something monolithic from a sequence and a separator

That's true, but we are using vec::join or slice::join and that respects the pattern matching and it is not monolithic. Then vec::join should be changed too. If Rust already acknowledges that join be non monolithic, then I suggest we stick with the same ideology.

But if I have an iterator of &strs, and the method is named join, I feel like it's a little less clear. Without looking further into it, it might seem like that I'd get a concatenated String back, or similar.

I'm always collecting to String after intersperse. sweat_smile

My personal preference aside, vec::join creates a vec if the initial data type is not &str. So it is just intersperse which happens to create a String in a specific scenario.

Personally, if it were not as verbose I would have gone for intersperse. If join was not used in vec or slice, I would not root for it. But weave doesn't sound good IMO.

My main point is that let's not have two different terms do the same thing based on datatype.

Copy link

Contributor

pickfire commented on Jun 30

edited

Personally, if it were not as verbose I would have gone for intersperse. If join was not used in vec or slice, I would not root for it. But weave doesn't sound good IMO.

I did join for Iterator in the past #75738 which accepts the current Join that does both but it wasn't accepted because it requires allocation or Iterator is in std so it couldn't be implemented, splitting it as two functions works but may not be that discoverable.

In a sample code in helix,

        let res = sel
            .ranges
            .into_iter()
            .map(|range| format!("{}/{}", range.anchor, range.head))
            .collect::<Vec<String>>()
            .join(",");

How to join String or &String easily with &str and such? I think cases more than that we need some intervention which the API will not be ergonomic. I think maybe it only works well with &'static str.

In the current example, I believe it's too basic and limited, requires &'static str and copied.

let hello = ["Hello", "World", "!"].iter().copied().intersperse(" ").collect::<String>();

I think we need to think more about the ergonomics of the API before stabilization.

Shouldn't there also be something like flat_intersperse? (intersperse but with an array (impl IntoIterator) as the argument)

Copy link

overlisted commented 29 days ago

edited

Continuing the flat_intersperse discussion myself, here's one way how it can be implemented (TODO make that Vec a generic list):

enum Step {
    PutNext,
    PutSeparator(usize),
}

pub struct FlatIntersperse<I: Iterator>
where
    I::Item: Clone,
{
    separator: Vec<I::Item>,
    iter: Peekable<I>,
    step: Step,
}

impl<I: Iterator> FlatIntersperse<I>
where
    I::Item: Clone,
{
    pub fn new(iter: I, separator: Vec<I::Item>) -> Self {
        Self {
            iter: iter.peekable(),
            separator,
            step: Step::PutNext,
        }
    }
}

impl<I> Iterator for FlatIntersperse<I>
where
    I: Iterator,
    I::Item: Clone,
{
    type Item = I::Item;

    fn next(&mut self) -> Option<Self::Item> {
        if self.iter.peek().is_none() {
            None
        } else {
            match self.step {
                Step::PutNext => {
                    self.step = Step::PutSeparator(0);

                    self.iter.next()
                }
                Step::PutSeparator(i) => {
                    let next_i = i + 1;

                    self.step = if next_i == self.separator.len() {
                        Step::PutNext
                    } else {
                        Step::PutSeparator(next_i)
                    };

                    Some(self.separator[i].clone())
                }
            }
        }
    }
}

Copy link

Member

m-ou-se commented 12 days ago

@rfcbot merge

Copy link

rfcbot commented 12 days ago

edited by dtolnay

Team member @m-ou-se has proposed to merge this. The next step is review by the rest of the tagged team members:

No concerns currently listed.

Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up!

See this document for info about what commands tagged team members can give me.

Copy link

rfcbot commented 7 days ago

bellThis is now entering its final comment period, as per the review above. bell

Copy link

SoniEx2 commented 7 days ago

could call it "sep" for separator?


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK