C# 使用 for each 时识别最后一个循环

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1068110/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 07:27:20  来源:igfitidea点击:

Identifying last loop when using for each

c#javarubyloopsforeach

提问by Matt

I want to do something different with the last loop iteration when performing 'foreach' on an object. I'm using Ruby but the same goes for C#, Java etc.

在对象上执行“foreach”时,我想对最后一次循环迭代做一些不同的事情。我正在使用 Ruby,但 C#、Java 等也是如此。

  list = ['A','B','C']
  list.each{|i|
    puts "Looping: "+i # if not last loop iteration
    puts "Last one: "+i # if last loop iteration
  }

The output desired is equivalent to:

所需的输出相当于:

  Looping: 'A'
  Looping: 'B'
  Last one: 'C'

The obvious workaround is to migrate the code to a for loop using 'for i in 1..list.length', but the for each solution feels more graceful. What is the most graceful way to code a special case during a loop? Can it be done with foreach?

显而易见的解决方法是使用 将代码迁移到 for 循环'for i in 1..list.length',但 for each 解决方案感觉更优雅。在循环期间对特殊情况进行编码的最优雅方式是什么?可以用foreach完成吗?

采纳答案by anonymous

How about obtaining a referenceto the last itemfirst and then use it for comparison inside the foreach loop? I am not say that you should do this as I myself would use the index based loop as mentioned by KlauseMeier. And sorry I don't know Ruby so the following sample is in C#! Hope u dont mind :-)

如何先获取对最后一项引用,然后在 foreach 循环内使用它进行比较?我并不是说你应该这样做,因为我自己会使用 KlauseMeier 提到的基于索引的循环。抱歉,我不懂 Ruby,所以下面的示例是用 C# 编写的!希望你不要介意:-)

string lastItem = list[list.Count - 1];
foreach (string item in list) {
    if (item != lastItem)
        Console.WriteLine("Looping: " + item);
    else    Console.Writeline("Lastone: " + item);
}

I revised the following code to compare by reference not value (can only use reference types not value types). the following code should support multiple objects containing same string (but not same string object) since MattChurcy's example did not specify that the strings must be distinct and I used LINQ Last method instead of calculating the index.

我修改了以下代码以通过引用而不是值进行比较(只能使用引用类型而不是值类型)。以下代码应该支持包含相同字符串(但不是相同字符串对象)的多个对象,因为 MattChurcy 的示例没有指定字符串必须是不同的,并且我使用了 LINQ Last 方法而不是计算索引。

string lastItem = list.Last();
foreach (string item in list) {
    if (!object.ReferenceEquals(item, lastItem))
        Console.WriteLine("Looping: " + item);
    else        Console.WriteLine("Lastone: " + item);
}

Limitations of the above code. (1) It can only work for strings or reference types not value types. (2) Same object can only appear once in the list. You can have different objects containing the same content. Literal strings cannot be used repeatedly since C# does not create a unique object for strings that have the same content.

上述代码的局限性。(1) 它只适用于字符串或引用类型,而不适用于值类型。(2) 同一个对象在列表中只能出现一次。您可以拥有包含相同内容的不同对象。文字字符串不能重复使用,因为 C# 不会为具有相同内容的字符串创建唯一对象。

And i no stupid. I know an index based loop is the one to use. I already said so when i first posted the initial answer. I provided the best answer I can in the contextof the question. I am too tired to keep explaining this so can you all just vote to delete my answer. I'll be so happy if this one goes away. thanks

而且我不傻。我知道可以使用基于索引的循环。当我第一次发布初始答案时,我已经说过了。我在问题的上下文中提供了最好的答案。我太累了,无法继续解释这一点,所以你们可以投票删除我的答案。如果这件事消失了,我会很高兴。谢谢

回答by Lazarus

Foreach is elegant in that it has no concern for the number of items in a list and treats each element equally, I think your only solution will be using a for loop that either stops at itemcount-1 and then you present your last item outside of the loop or a conditional within the loop that handles that specific condition, i.e. if (i==itemcount) { ... } else { ... }

Foreach 很优雅,因为它不关心列表中的项目数量,并且平等对待每个元素,我认为您唯一的解决方案是使用 for 循环,该循环要么在 itemcount-1 处停止,然后您将最后一个项目呈现在处理该特定条件的循环或循环内的条件,即 if (i==itemcount) { ... } else { ... }

回答by DanielR

At least in C# that's not possible without a regular for loop.

至少在 C# 中,如果没有常规的 for 循环,这是不可能的。

The enumerator of the collection decides whether a next elements exists (MoveNext method), the loop doesn't know about this.

集合的枚举器决定是否存在下一个元素(MoveNext 方法),循环不知道这一点。

回答by kgiannakakis

Is this elegant enough? It assumes a non-empty list.

这够优雅吗?它假定一个非空列表。

  list[0,list.length-1].each{|i|
    puts "Looping:"+i # if not last loop iteration
  }
  puts "Last one:" + list[list.length-1]

回答by H2000

You should use foreachonly if you handle eachone same. Use index based interation instead. Else you must add a different structure around the items, which you can use to differentiate the normal from last one in the foreach call (look at good Papers about the map reduced from google for the background: http://labs.google.com/papers/mapreduce.html, map == foreach, reduced == e.g. sum or filter).

只有在处理一个都相同时才应该使用foreach。改用基于索引的交互。否则,您必须在项目周围添加不​​同的结构,您可以使用它来区分 foreach 调用中的正常与最后一个(查看有关从谷歌减少的背景地图的好论文:http: //labs.google.com /papers/mapreduce.html,map == foreach,reduced == 例如 sum 或 filter)。

Map has no knowledge about the structure (esp. which position a item is), it only transforms one item by item (no knowledge from one item can be used to transform an other!), but reduce can use a memory to for example count the position and handle the last item.

Map 没有关于结构的知识(尤其是一个 item 的位置),它只对一个 item 进行转换(没有一个 item 的知识可以用来转换另一个!),但是 reduce 可以使用内存来例如 count位置并处理最后一个项目。

A common trick is to reverse the list and handle the first (which has now a known index = 0), and later apply reverse again. (Which is elegant but not fast ;) )

一个常见的技巧是反转列表并处理第一个(现在已知索引 = 0),然后再次应用 reverse 。(这很优雅但不快;))

回答by Michael Borgwardt

The foreach construct (in Java definitely, probably also in other languages) is intended to represent the most general kind if iteration, which includes iteration over collections that have no meaningful iteration order. For example, a hash-based set does not have an ordering, and therefore there is no"last element". The last iteration may yield a different element each time you iterate.

foreach 构造(当然在 Java 中,也可能在其他语言中)旨在表示最通用的 if 迭代,其中包括对没有有意义的迭代顺序的集合的迭代。例如,基于散列的集合没有排序,因此没有“最后一个元素”。每次迭代时,最后一次迭代可能会产生不同的元素。

Basically: no, the foreach construct is not meant to be used that way.

基本上:不,foreach 构造不打算以这种方式使用。

回答by Thomas Levesque

You could do something like that (C#) :

你可以做这样的事情(C#):

string previous = null;
foreach(string item in list)
{
    if (previous != null)
        Console.WriteLine("Looping : {0}", previous);
    previous = item;
}
if (previous != null)
    Console.WriteLine("Last one : {0}", previous);

回答by Svante

You can define an eachwithlastmethod in your class to do the same as eachon all elements but the last, but something else for the last:

您可以eachwithlast在您的类中定义一个方法来each对除最后一个元素之外的所有元素执行相同的操作,但对最后一个元素执行其他操作:

class MyColl
  def eachwithlast
    for i in 0...(size-1)
      yield(self[i], false)
    end
    yield(self[size-1], true)
  end
end

Then you could call it like this (foobeing an instance of MyCollor a subclass thereof):

然后你可以这样称呼它(foo作为其实例MyColl或其子类):

foo.eachwithlast do |value, last|
  if last
    puts "Last one: "+value
  else
    puts "Looping: "+value
  end
end

Edit:Following molf's suggestion:

编辑:按照莫尔夫的建议:

class MyColl
  def eachwithlast (defaultAction, lastAction)
    for i in 0...(size-1)
      defaultAction.call(self[i])
    end
    lastAction.call(self[size-1])
  end
end

foo.eachwithlast(
    lambda { |x| puts "looping "+x },
    lambda { |x| puts "last "+x } )

回答by klew

Ruby also has each_index method:

Ruby 也有 each_index 方法:

list = ['A','B','C']
list.each_index{|i|
  if i < list.size - 1
    puts "Looping:"+list[i]
  else
    puts "Last one:"+list[i]
}

EDIT:

编辑:

Or using each (corrected TomatoGG and Kirschstein solution):

或者使用每个(更正的 TomatoGG 和 Kirschstein 解决方案):

list = ['A', 'B', 'C', 'A']
list.each { |i|
   if (i.object_id != list.last.object_id)
      puts "Looping:#{i}"
   else
      puts "Last one:#{i}"
   end
}

Looping:A
Looping:B
Looping:C
Last one:A

Or

或者

list = ['A', 'B', 'C', 'A']
list.each {|i| 
  i.object_id != list.last.object_id ? puts "Looping:#{i}" : puts "Last one:#{i}"       
}

回答by Kirschstein

I think I prefer kgiannakakis's solution, however you could always do something like this;

我想我更喜欢kgiannakakis的解决方案,但是你总是可以做这样的事情;

list = ['A','B','C']
list.each { |i|
   if (i != list.last)
      puts "Looping:#{i}"
   else
      puts "Last one:#{i}"
   end
}