Дополнительное примечание к get_python_lib
функция, упомянутая уже: на некоторых платформах различные каталоги используются для платформы определенные модули (например: модули, которые требуют компиляции). Если Вы передаете plat_specific=True
функции, Вы получаете пакеты сайта для платформы определенные пакеты.
Well, it partly depends on the exact type of list
. It will also depend on the exact CLR you're using.
Whether it's in any way significant or not will depend on whether you're doing any real work in the loop. In almost all cases, the difference to performance won't be significant, but the difference to readability favours the foreach
loop.
I'd personally use LINQ to avoid the "if" too:
foreach (var item in list.Where(condition))
{
}
EDIT: For those of you who are claiming that iterating over a List
with foreach
produces the same code as the for
loop, here's evidence that it doesn't:
static void IterateOverList(List<object> list)
{
foreach (object o in list)
{
Console.WriteLine(o);
}
}
Produces IL of:
.method private hidebysig static void IterateOverList(class [mscorlib]System.Collections.Generic.List`1<object> list) cil managed
{
// Code size 49 (0x31)
.maxstack 1
.locals init (object V_0,
valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<object> V_1)
IL_0000: ldarg.0
IL_0001: callvirt instance valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<!0> class [mscorlib]System.Collections.Generic.List`1<object>::GetEnumerator()
IL_0006: stloc.1
.try
{
IL_0007: br.s IL_0017
IL_0009: ldloca.s V_1
IL_000b: call instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<object>::get_Current()
IL_0010: stloc.0
IL_0011: ldloc.0
IL_0012: call void [mscorlib]System.Console::WriteLine(object)
IL_0017: ldloca.s V_1
IL_0019: call instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<object>::MoveNext()
IL_001e: brtrue.s IL_0009
IL_0020: leave.s IL_0030
} // end .try
finally
{
IL_0022: ldloca.s V_1
IL_0024: constrained. valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<object>
IL_002a: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_002f: endfinally
} // end handler
IL_0030: ret
} // end of method Test::IterateOverList
The compiler treats arrays differently, converting a foreach
loop basically to a for
loop, but not List
. Here's the equivalent code for an array:
static void IterateOverArray(object[] array)
{
foreach (object o in array)
{
Console.WriteLine(o);
}
}
// Compiles into...
.method private hidebysig static void IterateOverArray(object[] 'array') cil managed
{
// Code size 27 (0x1b)
.maxstack 2
.locals init (object V_0,
object[] V_1,
int32 V_2)
IL_0000: ldarg.0
IL_0001: stloc.1
IL_0002: ldc.i4.0
IL_0003: stloc.2
IL_0004: br.s IL_0014
IL_0006: ldloc.1
IL_0007: ldloc.2
IL_0008: ldelem.ref
IL_0009: stloc.0
IL_000a: ldloc.0
IL_000b: call void [mscorlib]System.Console::WriteLine(object)
IL_0010: ldloc.2
IL_0011: ldc.i4.1
IL_0012: add
IL_0013: stloc.2
IL_0014: ldloc.2
IL_0015: ldloc.1
IL_0016: ldlen
IL_0017: conv.i4
IL_0018: blt.s IL_0006
IL_001a: ret
} // end of method Test::IterateOverArray
Interestingly, I can't find this documented in the C# 3 spec anywhere...
Простой тест для полуутверждения. Я сделал небольшой тест, просто чтобы увидеть. Вот код:
static void Main(string[] args)
{
List<int> intList = new List<int>();
for (int i = 0; i < 10000000; i++)
{
intList.Add(i);
}
DateTime timeStarted = DateTime.Now;
for (int i = 0; i < intList.Count; i++)
{
int foo = intList[i] * 2;
if (foo % 2 == 0)
{
}
}
TimeSpan finished = DateTime.Now - timeStarted;
Console.WriteLine(finished.TotalMilliseconds.ToString());
Console.Read();
}
А вот секция foreach:
foreach (int i in intList)
{
int foo = i * 2;
if (foo % 2 == 0)
{
}
}
Когда я заменил for на foreach - foreach был на 20 миллисекунд быстрее - последовательно . Значение for составляло 135–139 мс, а значение foreach - 113–119 мс. Я несколько раз менял местами, чтобы убедиться, что это не какой-то процесс, который только что запустился.
Однако, когда я удалил foo и оператор if, for было быстрее на 30 мс (foreach было 88 мс, а for было 59 мс). Оба были пустыми снарядами. Я предполагаю, что foreach действительно передал переменную, в то время как for просто увеличивал переменную. Если я добавил
int foo = intList[i];
, то for станет медленнее примерно на 30 мс. Я предполагаю, что это было связано с созданием foo, захватом переменной в массиве и присвоением ее foo. Расчет для цикла: 2458 мс Вычисление цикла foreach: 2005 мс
Их перестановка, чтобы увидеть, работает ли он с порядком вещей, дает те же результаты (почти).
Примечание: этот ответ больше относится к Java, чем к C #, поскольку в C # нет индексатора для LinkedLists
, но я думаю, что общая точка зрения все еще сохраняется.
Если список
, с которым вы работаете, оказывается LinkedList
, производительность кода индексатора (доступ в стиле массива ) является намного хуже, чем использование IEnumerator
из foreach
для больших списков.
При доступе к элементу 10.000 в LinkedList
с использованием синтаксиса индексатора: ] list [10000]
, связанный список начнется с головного узла и пройдет десять тысяч раз по указателю Next
, пока не достигнет нужного объекта. Очевидно, если вы сделаете это в цикле, вы получите:
list[0]; // head
list[1]; // head.Next
list[2]; // head.Next.Next
// etc.
Когда вы вызываете GetEnumerator
(неявно используя синтаксис forach
), вы получаете объект IEnumerator
, который имеет указатель на головной узел. Каждый раз, когда вы вызываете MoveNext
, этот указатель перемещается на следующий узел, например:
IEnumerator em = list.GetEnumerator(); // Current points at head
em.MoveNext(); // Update Current to .Next
em.MoveNext(); // Update Current to .Next
em.MoveNext(); // Update Current to .Next
// etc.
Как видите, в случае LinkedList
s метод индексатора массива становится чем медленнее и медленнее, тем длиннее цикл (он должен проходить через один и тот же указатель головы снова и снова). В то время как IEnumerable
просто работает в постоянное время.
Конечно, как сказал Джон, это действительно зависит от типа списка
, если список
не a LinkedList
, но массив, поведение совершенно другое.
IEnumerator
, который имеет указатель на головной узел. Каждый раз, когда вы вызываете MoveNext
, этот указатель перемещается на следующий узел, например:
IEnumerator em = list.GetEnumerator(); // Current points at head
em.MoveNext(); // Update Current to .Next
em.MoveNext(); // Update Current to .Next
em.MoveNext(); // Update Current to .Next
// etc.
Как видите, в случае LinkedList
s метод индексатора массива становится чем медленнее и медленнее, тем длиннее цикл (он должен проходить через один и тот же указатель головы снова и снова). В то время как IEnumerable
просто работает в постоянное время.
Конечно, как сказал Джон, это действительно зависит от типа списка
, если список
не a LinkedList
, но массив, поведение совершенно другое.
IEnumerator
, который имеет указатель на головной узел. Каждый раз, когда вы вызываете MoveNext
, этот указатель перемещается на следующий узел, например:
IEnumerator em = list.GetEnumerator(); // Current points at head
em.MoveNext(); // Update Current to .Next
em.MoveNext(); // Update Current to .Next
em.MoveNext(); // Update Current to .Next
// etc.
Как видите, в случае LinkedList
s метод индексатора массива становится чем медленнее и медленнее, тем длиннее цикл (он должен проходить через один и тот же указатель головы снова и снова). В то время как IEnumerable
просто работает в постоянное время.
Конечно, как сказал Джон, это действительно зависит от типа списка
, если список
не a LinkedList
, но массив, поведение совершенно другое.
IEnumerable
просто работает в постоянное время.
Конечно, как сказал Джон, это действительно зависит от типа списка
, если список
не a LinkedList
, но массив, поведение совершенно другое.
IEnumerable
просто работает в постоянное время.
Конечно, как сказал Джон, это действительно зависит от типа списка
, если список
не a LinkedList
, но массив, поведение совершенно другое.
Как уже упоминали другие люди, хотя производительность на самом деле не имеет большого значения, foreach всегда будет немного медленнее из-за IEnumerable
/ IEnumerator
использование в цикле. Компилятор переводит эту конструкцию в вызовы этого интерфейса, и для каждого шага в конструкции foreach вызываются функция + свойство
IEnumerator iterator = ((IEnumerable)list).GetEnumerator();
while (iterator.MoveNext()) {
var item = iterator.Current;
// do stuff
}
. Это эквивалентное расширение конструкции в C #. Вы можете себе представить, как влияние на производительность может варьироваться в зависимости от реализации MoveNext и Current. В то время как при доступе к массиву у вас нет этих зависимостей.
Это ситуация, когда «научите человека ловить рыбу» - гораздо более полезный подход, чем «дай человеку рыбу». Как только вы поймете основные принципы и API NSDictionary, станет намного проще разработать собственное решение. Вот несколько наблюдений и моментов для обучения:
+ dictionaryWithObject: forKey:
используется для создания NSDictionary с одной парой ключ-значение. Он не будет принимать аргументы, разделенные запятыми, после каждого двоеточия (:) в вызове метода, только один. Чтобы создать словарь с несколькими парами «ключ-значение», используйте один из двух связанных методов: + dictionaryWithObjects: forKeys:
, который принимает два объекта NSArray, содержащих значения и ключи, или + dictionaryWithObjectsAndKeys:
, который заменяет (объект, ключ, объект, ключ) завершающим аргументом nil
. -copy
. (Помните, что метод копирования возвращает новый объект, который вы должны освободить, чтобы избежать утечки памяти.) Таким образом, вам не нужно иметь переменную для каждого отдельного значения, поэтому вы можете выполнить «одноразовое» создание структуры (s ) на каждом уровне. + arrayWithObject:
для создания NSArray с одним объектом. private static IEnumerable<T> MyEnum(List<T> list)
{
for (int i = 0; i < list.Count; i++)
{
yield return list[i];
}
}
Итак, как вы можете видеть, в этом случае это не будет иметь большого значения, однако перечислитель для связанного списка, вероятно, будет что-то искать примерно так:
private static IEnumerable<T> MyEnum(LinkedList<T> list)
{
LinkedListNode<T> current = list.First;
do
{
yield return current.Value;
current = current.Next;
}
while (current != null);
}
В .NET вы обнаружите, что класс LinkedList
public T this[int index]
{
LinkedListNode<T> current = this.First;
for (int i = 1; i <= index; i++)
{
current = current.Next;
}
return current.value;
}
Как видите, многократный вызов этого цикла в цикле будет намного медленнее, чем использование счетчика, который может запомнить, где он находится в списке.
однако перечислитель для связанного списка, вероятно, будет выглядеть примерно так:private static IEnumerable<T> MyEnum(LinkedList<T> list)
{
LinkedListNode<T> current = list.First;
do
{
yield return current.Value;
current = current.Next;
}
while (current != null);
}
В .NET вы обнаружите, что класс LinkedList
public T this[int index]
{
LinkedListNode<T> current = this.First;
for (int i = 1; i <= index; i++)
{
current = current.Next;
}
return current.value;
}
Как видите, многократный вызов этого цикла в цикле будет намного медленнее, чем использование счетчика, который может запомнить, где он находится в списке.
однако перечислитель для связанного списка, вероятно, будет выглядеть примерно так:private static IEnumerable<T> MyEnum(LinkedList<T> list)
{
LinkedListNode<T> current = list.First;
do
{
yield return current.Value;
current = current.Next;
}
while (current != null);
}
В .NET вы обнаружите, что класс LinkedList
public T this[int index]
{
LinkedListNode<T> current = this.First;
for (int i = 1; i <= index; i++)
{
current = current.Next;
}
return current.value;
}
Как видите, многократный вызов этого цикла в цикле будет намного медленнее, чем использование счетчика, который может запомнить, где он находится в списке.