What Happened When Pass a Method to Iterator Method

what happened when pass a method to iterator method

Here's what works. Explanation below.

class String
def rettwo
self + self
end
end

def a_simple_method &proc
proc.call('a')
end

def a_iterator_method
yield 'b'
end

a_simple_method(&:rettwo) # => "aa"
a_iterator_method(&:rettwo) # => "bb"

The &: construct is called Symbol#to_proc. It turns symbol into a proc. This proc expects a receiver as a first argument. The remaining arguments are used to call the proc. You're not passing any arguments, hence the "receiver not given" error.

Here's a demonstration of additional arguments:

class String
def say name
"#{self} #{name}"
end
end

def a_simple_method &proc
proc.call('hello', 'ruby')
end

a_simple_method(&:say) # => "hello ruby"

Here's a definition of Symbol#to_proc from some blog post from 2008. Modern Symbol#to_proc seems to be implemented in C, but this can still help the understanding.

class Symbol
def to_proc
Proc.new { |*args| args.shift.__send__(self, *args) }
end
end

Passing Iterator to method

Can anyone tell me what I'm doing wrong that it's trying to call the " U save(U entity);" method instead of the " Iterable save(Iterable entities);" method?

Well save(Iterable<U> entities) clearly isn't valid here, because you have an Iterator<E>, not an Iterable<E>. They're different types. Given two method overloads which are both invalid for the arguments that you're trying to pass, the compiler has picked one to complain about... it doesn't really matter much which it says is invalid, IMO.

It's not clear why you're calling iterator() at all, but you should try just changing your code to:

public void persistLandTiles(List<LandTile> tilesToPersist) {
landTileRepo.save(tilesToPersist);
}

After all, List<E> extends Iterable<E> - so this method call will be valid...

How do I pass an iterator I am iterating on to a function?

The for construct consumes the iterator, and doing what you want using it will be quite tricky (if not impossible, I'm really not sure about that).

However, you can have it working pretty easily by switching to a while let construct, like this:

fn parse_tokens (tokens: &Vec<char>) {
let mut iter = tokens.iter();
let mut things: Vec<Thing> = vec![];
while let Some(token) = iter.next() {
match token {
&'a' => things.push(take_three(&mut iter)),
&'b' => things.push(take_four(&mut iter)),
_ => {},
}
}
}

Passing vector iterator to a function c++

it is an iterator object, passing it as-is would mean you're trying to pass an object of type vector<tObj>::iterator for a function expecting tObj*, and thus the error.

When you do *it you'd get the underlying object the iterator is representing and when you apply & atop that, you get that object's address, which is of type tObj* which agrees with the function's argument type and thus no error.

Pass iterator as a function parameter

The particular error you get is because you'd need a template template argument:

template<template <typename> class C, typename T>
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^
T sum( C<T>::iterator begin, C<T>::iterator end )

However, the standard containers typically have more than just one template argument:

template < class T, class Alloc = allocator<T> > class vector

and it is a bit non-trivial to write such function correctly. You could use variadic template arguments, or you could do like the standard library does, and only specialize as much as you really need:

// <algorithm>
namespace std {
template <class RandomAccessIterator>
void sort (RandomAccessIterator first, RandomAccessIterator last);
}

In your case (pretending that your need is not covered by the standard algorithms library already):

template <typename Iterator>
auto sum(Iterator begin, Iterator end)
-> decltype(*begin+*begin) // the type of summing two of them
{
if (begin == end) throw std::logic_error("....");
auto s = *begin;
++begin;
for (; begin != end; ++begin) {
s += *begin;
}
return s;
}

There are some more differences from your original code:

  • the new code does not assume a null or a default constructor defined (T s = null;)
  • does not introduce additional iterator (it)
  • uses pre-increment
  • throws an exception when begin==end

If you add an init parameter, you can make it almost noexcept:

template <typename Iterator, typename T>
T sum(Iterator begin, Iterator end, T init)
{
for (; begin!=end; ++begin)
init += *begin;
return init;
}

But only almost, because init += *begin could still throw.

If you have such signature, you've by the way reproduced the signature of std::accumulate.

Passing an iterator and vector to a function

The iterator models a pointer, and it most likely either is one, or contains one which points to the vector or its contents. When you copy it, the copy is in fact a different iterator, but it stores the same value, so it still points to the same thing, just like a pointer would.

Pass iterable as method argument?

You need to pass an Iterable or one of its subtypes, e.g., List, Set, etc. So, you can add the HashMap to a List, and pass it:

HashMap<String,Date> items = new HashMap<String, Date>();
items.put("item1", new Date());
items.put("item2", new Date());

List<HashMap<String, Date>> list = new ArrayList<HashMap<String, Date>>();
list.add(items);

testMethod(list, "more", "data");

Note that since the parameter type is Iterable<HashMap<String, Date>>, you can only pass - List<HashMap...> or Set<HashMap...>, etc. You cannot pass a List<Map..> in it.

Can a iterator change the collection it is iterating over? Java

The Iterator simply provides an interface into some sort of stream, therefore not only is it perfectly possible for next() to destroy data in some way, but it's even possible for the data in an Iterator to be unique and irreplaceable.

We could come up with more direct examples, but an easy one is the Iterator in DirectoryStream. While a DirectoryStream is technically Iterable, it only allows one Iterator to be constructed, so if you tried to do the following:

Path dir = ...
try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {
int count = length(stream.iterator());
for (Path entry: stream) {
...
}
}

You would get an exception in the foreach block, because the stream can only be iterated once. So in summary, it is possible for your length() method to change objects and lose data.

Furthermore, there's no reason an Iterator has to be associated with some separate data-store. Take for example an answer I gave a few months ago providing a clean way to select n random numbers. By using an infinite Iterator we are able to provide, filter, and pass around arbitrarily large amounts of random data lazily, no need to store it all at once, or even compute them until they're needed. Because the Iterator doesn't back any data structure, querying it is obviously destructive.

Now that said, these examples don't make your method bad. Notice that the Guava library (which everyone should be using) provides an Iterators class with exactly the behavior you detail above, called size() to conform with the Collections Framework. The burden is then on the user of such methods to be aware of what sort of data they're working with, and avoid making careless calls such as trying to count the number of results in an Iterator that they know cannot be replaced.



Related Topics



Leave a reply



Submit