foreach (Person criminal in people.Where(person => person.isCriminal)
{
// do something
}
我有这段代码,想知道它是如何工作的。它是否等同于嵌套在foreach迭代中的if语句,还是首先循环遍历人员列表并使用选定的值重复循环?我希望从效率的角度更多地了解这一点。
foreach (Person criminal in people)
{
if (criminal.isCriminal)
{
// do something
}
}
答案 0 :(得分:3)
Where
使用延迟执行。
这意味着当您致电Where
时,不会立即进行过滤。相反,每次在GetEnumerator().MoveNext()
的返回值上调用Where
时,它都会检查序列中的下一个元素是否满足条件。如果没有,它会跳过此元素并检查下一个元素。如果有一个元素满足条件,它会停止前进,您可以使用Current
获取值。
基本上,就像在foreach
循环中使用if语句一样。
答案 1 :(得分:1)
要了解会发生什么,您必须知道IEnumerables<T>
如何工作(因为LINQ to Objects始终在IEnumerables<T>
上工作。IEnumerables<T>
返回一个实现迭代器的IEnumerator<T>
。迭代器是惰性的,即它总是只生成序列中的一个元素。除非你有一个OrderBy
或另一个需要它的命令,否则不会提前完成循环。
所以,如果你有......
foreach (string name in source.Where(x => x.IsChecked).Select(x => x.Name)) {
Console.WriteLine(name);
}
......会发生这种情况:foreach语句需要Select
请求的第一个项目,而Where
需要一个来自Select
的项目,而该项目又会从Where
中检索一个项目。资源。第一个名称将打印到控制台。
然后foreach语句需要import sys
import cv2
from PyQt5.QtCore import QThread, pyqtSignal, Qt
from PyQt5.QtGui import QPixmap, QImage
from PyQt5.QtWidgets import QApplication, QWidget, QLabel
import datetime
class App(QWidget):
def __init__(self):
super().__init__()
self.title = 'PyQt5 Video'
self.left = 100
self.top = 100
self.width = 640
self.height = 480
self.initUI()
def initUI(self):
self.setWindowTitle(self.title)
self.setGeometry(self.left, self.top, self.width, self.height)
self.resize(1800, 1200)
# create a label
label = QLabel(self)
label.move(280, 120)
label.resize(640, 480)
label1 = QLabel(self)
label1.move(680, 820)
th = Thread(self)
th.changePixmap.connect(lambda p: label.setPixmap(p))
th.changeLabel.connect(lambda n:label1.setText("A"))
th.start()
class Thread(QThread):
changePixmap = pyqtSignal(QPixmap)
changeLabel = pyqtSignal(QLabel)
def __init__(self, parent=None):
QThread.__init__(self, parent=parent)
def run(self):
cap = cv2.VideoCapture(0)
n=QLabel()
while True:
ret, frame = cap.read()
rgbImage = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
convertToQtFormat = QImage(rgbImage.data, rgbImage.shape[1], rgbImage.shape[0], QImage.Format_RGB888)
convertToQtFormat = QPixmap.fromImage(convertToQtFormat)
p = convertToQtFormat.scaled(640, 480, Qt.KeepAspectRatio)
self.changePixmap.emit(p)
now = datetime.datetime.now()
sec = now.second
try:
self.changeLabel.emit(n)
except Exception as e:
print(str(e))
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = App()
ex.show()
sys.exit(app.exec_())
请求的第二个项目,而(nSpend / 365)
则需要Function formula_test(dtCurrentDate As Date, dtEffectiveDate As Date, nDays As Long, nSpend As Double) As Double
On Error GoTo eHandle
If dtEffectiveDate > dtCurrentDate Then
formula_test = 0
Else
If ((dtCurrentDate - dtEffectiveDate) + 1) > nDays Then
formula_test = nDays * (nSpend / 365)
Else
formula_test = ((nSpend / 365) * (dtCurrentDate - dtEffectiveDate + 1))
If (dtCurrentDate - dtCurrentDate + 1) - nDays > 0 Then
formula_test = formula_test - nSpend / 365
End If
End If
End If
Exit Function
eHandle:
formula_test = 0
End Function
中的一个项目,而该项目又从源中检索一个项目。第二个名称将打印到控制台。
等等。
这意味着您的两个代码片段在逻辑上都是等效的。
答案 2 :(得分:0)
这取决于people
是什么。
如果people
是IEnumerable
对象(如集合,或使用yield
的方法的结果),那么您问题中的两段代码确实是等效的。
天真的Where
可以实现为:
public static IEnumerable<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
// Error handling left out for simplicity.
foreach (TSource item in source)
{
if (predicate(item))
{
yield return item;
}
}
}
Enumerable
中的实际代码有点不同,以确保传递空source
或predicate
的错误立即发生而不是延迟执行,并优化少数情况(例如source.Where(x => x.IsCriminal).Where(x => x.IsOnParole)
变为source.Where(x => x.IsCriminal && x.IsOnParole)
的等价物,因此迭代链中的步骤少一个),但这是基本原则。
但是,如果people
是IQueryable
,那么事情会有所不同,并且取决于相关查询提供程序的详细信息。
最简单的可能性是查询提供程序无法对Where
执行任何特殊操作,因此它最终只执行上述操作,因为它仍然有用。
但查询提供商通常可以做其他事情。假设people
是实体框架中的DbSet<Person>
,与名为people
的数据库中的表相关联。如果你这样做:
foreach(var person in people)
{
DoSomething(person);
}
然后Entity Framework将运行类似于SQL的SQL:
SELECT *
FROM people
然后为返回的每一行创建一个Person
对象。我们可以在即将实施Where
时进行相同的过滤,但我们也可以做得更好。
如果你这样做:
foreach (Person criminal in people.Where(person => person.isCriminal)
{
DoSomething(person);
}
然后Entity Framework将运行类似于SQL的SQL:
SELECT *
FROM people
WHERE isCriminal = 1
这意味着决定返回哪些元素的逻辑是在数据库返回.NET之前完成的。它允许在计算WHERE
时使用索引,这可以更有效,但即使在没有有用索引的情况下,数据库必须进行全面扫描,它仍然意味着那些记录我们不关心从来没有从数据库中报告过,并且没有为它们创建的对象只是为了再次丢弃,所以性能的差异可能是巨大的。
我希望从效率的角度了解更多信息
您可能会感到满意,因为您建议可能会发生双重传递,并且很高兴知道它比您建议的foreach … if
更有效。
裸foreach
和if
仍然会.Where()
对IEnumerable
(但不会针对数据库来源),因为{{1}会有一些开销} Where
和foreach
没有,但它只是在一个非常热门的路径中值得关注的程度。通常if
可以合理放心地使用Where
。