recorrer - ¿Cómo llenar/instanciar una matriz C#con un solo valor?
matrices en c# windows form (20)
Sé que las matrices instanciadas de tipos de valores en C # se completan automáticamente con el valor predeterminado del tipo (por ejemplo, falso para bool, 0 para int, etc.).
¿Hay alguna forma de completar automáticamente una matriz con un valor inicial que no sea el predeterminado? Ya sea en la creación o en un método incorporado después (como Arrays.fill() Java) Digamos que quería una matriz booleana que fuera verdadera por defecto, en lugar de falsa. ¿Hay una forma incorporada de hacerlo, o solo tienes que iterar a través de la matriz con un ciclo for?
// Example pseudo-code:
bool[] abValues = new[1000000];
Array.Populate(abValues, true);
// Currently how I''m handling this:
bool[] abValues = new[1000000];
for (int i = 0; i < 1000000; i++)
{
abValues[i] = true;
}
Tener que iterar a través de la matriz y "restablecer" cada valor a verdadero parece ineficaz. ¿Hay alguna manera alrededor de esto? Tal vez volteando todos los valores?
Después de escribir esta pregunta y pensar en ello, supongo que los valores predeterminados son simplemente el resultado de cómo C # maneja la asignación de memoria de estos objetos entre bastidores, así que imagino que probablemente no sea posible hacerlo. ¡Pero aún me gustaría saberlo con certeza!
¿Qué tal una implementación paralela?
public static void InitializeArray<T>(T[] array, T value)
{
var cores = Environment.ProcessorCount;
ArraySegment<T>[] segments = new ArraySegment<T>[cores];
var step = array.Length / cores;
for (int i = 0; i < cores; i++)
{
segments[i] = new ArraySegment<T>(array, i * step, step);
}
var remaining = array.Length % cores;
if (remaining != 0)
{
var lastIndex = segments.Length - 1;
segments[lastIndex] = new ArraySegment<T>(array, lastIndex * step, array.Length - (lastIndex * step));
}
var initializers = new Task[cores];
for (int i = 0; i < cores; i++)
{
var index = i;
var t = new Task(() =>
{
var s = segments[index];
for (int j = 0; j < s.Count; j++)
{
array[j + s.Offset] = value;
}
});
initializers[i] = t;
t.Start();
}
Task.WaitAll(initializers);
}
Cuando solo se inicializa una matriz, no se puede ver el poder de este código, pero creo que definitivamente debes olvidarte de lo "puro".
Aquí hay otro apprate con System.Collections.BitArray
que tiene dicho constructor.
bool[] result = new BitArray(1000000, true).Cast<bool>().ToArray();
o
bool[] result = new bool[1000000];
new BitArray(1000000, true).CopyTo(result, 0);
Bueno, después de un poco más de googlear y leer, encontré esto:
bool[] bPrimes = new bool[1000000];
bPrimes = Array.ConvertAll<bool, bool>(bPrimes, b=> b=true);
Lo cual está ciertamente más cerca de lo que estoy buscando. Pero no estoy seguro de si eso es mejor que iterar a través de la matriz original en un bucle for y simplemente cambiar los valores. Después de una prueba rápida, de hecho, parece ser más lenta en un factor de 5. ¡Entonces realmente no es una buena solución!
Crea una nueva matriz con mil valores true
:
var items = Enumerable.Repeat<bool>(true, 1000).ToArray(); // Or ToList(), etc.
Del mismo modo, puedes generar secuencias enteras:
var items = Enumerable.Range(0, 1000).ToArray(); // 0..999
Desafortunadamente, no creo que haya una manera directa, sin embargo, creo que puedes escribir un método de extensión para la clase de matriz para hacer esto.
class Program
{
static void Main(string[] args)
{
int[] arr = new int[1000];
arr.Init(10);
Array.ForEach(arr, Console.WriteLine);
}
}
public static class ArrayExtensions
{
public static void Init<T>(this T[] array, T defaultVaue)
{
if (array == null)
return;
for (int i = 0; i < array.Length; i++)
{
array[i] = defaultVaue;
}
}
}
El siguiente código combina iteración simple para copias pequeñas y Array.Copy para copias grandes
public static void Populate<T>( T[] array, int startIndex, int count, T value ) {
if ( array == null ) {
throw new ArgumentNullException( "array" );
}
if ( (uint)startIndex >= array.Length ) {
throw new ArgumentOutOfRangeException( "startIndex", "" );
}
if ( count < 0 || ( (uint)( startIndex + count ) > array.Length ) ) {
throw new ArgumentOutOfRangeException( "count", "" );
}
const int Gap = 16;
int i = startIndex;
if ( count <= Gap * 2 ) {
while ( count > 0 ) {
array[ i ] = value;
count--;
i++;
}
return;
}
int aval = Gap;
count -= Gap;
do {
array[ i ] = value;
i++;
--aval;
} while ( aval > 0 );
aval = Gap;
while ( true ) {
Array.Copy( array, startIndex, array, i, aval );
i += aval;
count -= aval;
aval *= 2;
if ( count <= aval ) {
Array.Copy( array, startIndex, array, i, count );
break;
}
}
}
Los puntos de referencia para diferentes longitudes de matriz usando una matriz int [] son:
2 Iterate: 1981 Populate: 2845
4 Iterate: 2678 Populate: 3915
8 Iterate: 4026 Populate: 6592
16 Iterate: 6825 Populate: 10269
32 Iterate: 16766 Populate: 18786
64 Iterate: 27120 Populate: 35187
128 Iterate: 49769 Populate: 53133
256 Iterate: 100099 Populate: 71709
512 Iterate: 184722 Populate: 107933
1024 Iterate: 363727 Populate: 126389
2048 Iterate: 710963 Populate: 220152
4096 Iterate: 1419732 Populate: 291860
8192 Iterate: 2854372 Populate: 685834
16384 Iterate: 5703108 Populate: 1444185
32768 Iterate: 11396999 Populate: 3210109
La primera columna es el tamaño de la matriz, seguido por el tiempo de copia usando una iteración simple (implementación @JaredPared). El tiempo de este método es después de eso. Estos son los puntos de referencia usando una matriz de una estructura de cuatro enteros
2 Iterate: 2473 Populate: 4589
4 Iterate: 3966 Populate: 6081
8 Iterate: 7326 Populate: 9050
16 Iterate: 14606 Populate: 16114
32 Iterate: 29170 Populate: 31473
64 Iterate: 57117 Populate: 52079
128 Iterate: 112927 Populate: 75503
256 Iterate: 226767 Populate: 133276
512 Iterate: 447424 Populate: 165912
1024 Iterate: 890158 Populate: 367087
2048 Iterate: 1786918 Populate: 492909
4096 Iterate: 3570919 Populate: 1623861
8192 Iterate: 7136554 Populate: 2857678
16384 Iterate: 14258354 Populate: 6437759
32768 Iterate: 28351852 Populate: 12843259
Haga una clase privada dentro de donde hace la matriz y obtenga un getter y setter para ella. A menos que necesite que cada posición en la matriz sea algo único, como aleatorio, entonces use int? como una matriz y luego obtener si la posición es igual a nulo llenar esa posición y devolver el nuevo valor aleatorio.
IsVisibleHandler
{
private bool[] b = new bool[10000];
public bool GetIsVisible(int x)
{
return !b[x]
}
public void SetIsVisibleTrueAt(int x)
{
b[x] = false //!true
}
}
O usar
public void SetIsVisibleAt(int x, bool isTrue)
{
b[x] = !isTrue;
}
Como setter.
Hay algunas respuestas más sobre esta pregunta (¿duplicado?): ¿Cuál es el equivalente de memset en C #?
Alguien ha evaluado las alternativas (incluyeron una versión insegura, pero no memset
): http://techmikael.blogspot.co.uk/2009/12/filling-array-with-default-value.html
Lo probé por mí mismo y parece que el método Enumerable.Repeat es diez veces más rápido.
[Test]
public void Create_array_with_Array_Copy() {
var size = 3000 * 30000;
var arr = new int [size];
var val = 0xFF;
FillArray<int> (arr, val);
}
[Test]
public void Create_array_with_loop () {
var size = 3000 * 30000;
var arr = new int [size];
var val = 0xFF;
Populate<int> (arr, val);
}
[Test]
public void Create_array_with_Repeat () {
var size = 3000 * 30000;
var arr = new int [size];
var val = 0xFF;
Enumerable.Repeat<int> (val, size);
}
static void FillArray<T> (T [] arr, T fillValue) {
int i = 0;
if (arr.Length > 16) {
{
do {
arr [i++] = fillValue;
} while (i < arr.Length);
while (i + 16 < arr.Length) {
Array.Copy (arr, 0, arr, i, 16);
i = i + 16;
}
}
while (i < arr.Length) {
arr [i++] = fillValue;
}
}
}
static void Populate<T> (T [] arr, T value) {
for (int i = 0; i < arr.Length; i++) {
arr [i] = value;
}
}
respeta a Ronny
Me doy cuenta de que llego tarde a la fiesta, pero esta es una idea. Escriba un contenedor que tenga operadores de conversión hacia y desde el valor envuelto para que pueda ser utilizado como un suplente para el tipo envuelto. Esto fue en realidad inspirado por la respuesta tonta de @ l33t.
Primero (viniendo de C ++) me di cuenta de que en C # no se llama a un ctor predeterminado cuando se construyen los elementos de una matriz. En cambio, ¡incluso en presencia de un constructor predeterminado definido por el usuario! - todos los elementos de la matriz tienen cero inicialización. Eso me sorprendió
Así que una clase contenedora que simplemente proporciona un ctor predeterminado con el valor deseado funcionaría para las matrices en C ++ pero no en C #. Una solución alternativa es permitir que el contenedor escriba el mapa 0 en el valor de inicialización deseado al realizar la conversión. De esta forma, los valores iniciales cero parecen inicializarse con la semilla para todos los propósitos prácticos:
public struct MyBool
{
private bool _invertedValue;
public MyBool(bool b)
{
_invertedValue = !b;
}
public static implicit operator MyBool(bool b)
{
return new MyBool(b);
}
public static implicit operator bool(MyBool mb)
{
return !mb._invertedValue;
}
}
static void Main(string[] args)
{
MyBool mb = false; // should expose false.
Console.Out.WriteLine("false init gives false: "
+ !mb);
MyBool[] fakeBoolArray = new MyBool[100];
Console.Out.WriteLine("Default array elems are true: "
+ fakeBoolArray.All(b => b) );
fakeBoolArray[21] = false;
Console.Out.WriteLine("Assigning false worked: "
+ !fakeBoolArray[21]);
fakeBoolArray[21] = true;
// Should define ToString() on a MyBool,
// hence the !! to force bool
Console.Out.WriteLine("Assigning true again worked: "
+ !!fakeBoolArray[21]);
}
Este patrón es aplicable a todos los tipos de valores. Uno podría, por ejemplo, mapear de 0 a 4 para las entradas si se deseaba la inicialización con 4, etc.
Me encantaría hacer una plantilla como sería posible en C ++, proporcionando el valor inicial como parámetro de plantilla, pero entiendo que eso no es posible en C #. ¿O me estoy perdiendo algo? (Por supuesto, en C ++ mapeo no es necesario en absoluto porque uno puede proporcionar un ctor predeterminado que se llamará para elementos de la matriz.)
FWIW, aquí hay un equivalente de C ++: https://ideone.com/wG8yEh .
No hay forma de establecer todos los elementos en una matriz como una operación única, A MENOS QUE ese valor sea el valor predeterminado de los tipos de elementos.
Por ejemplo, si se trata de una matriz de números enteros, puede ponerlos a cero con una sola operación, como Array.Clear(...)
: Array.Clear(...)
No sabe de un método de marco pero puede escribir un ayudante rápido para hacerlo por usted.
public static void Populate<T>(this T[] arr, T value ) {
for ( int i = 0; i < arr.Length;i++ ) {
arr[i] = value;
}
}
O ... podrías simplemente usar la lógica invertida. Deje que false
signifique true
y viceversa.
Muestra de código
// bool[] isVisible = Enumerable.Repeat(true, 1000000).ToArray();
bool[] isHidden = new bool[1000000]; // Crazy-fast initialization!
// if (isVisible.All(v => v))
if (isHidden.All(v => !v))
{
// Do stuff!
}
Para arreglos o matrices grandes que serán de tamaño variable, probablemente debas usar:
Enumerable.Repeat(true, 1000000).ToArray();
Para una matriz pequeña, puede usar la sintaxis de inicialización de la colección en C # 3:
bool[] vals = new bool[]{ false, false, false, false, false, false, false };
El beneficio de la sintaxis de inicialización de la colección es que no tiene que usar el mismo valor en cada ranura y puede usar expresiones o funciones para inicializar una ranura. Además, creo que evita el costo de inicializar la ranura de matriz al valor predeterminado. Así por ejemplo:
bool[] vals = new bool[]{ false, true, false, !(a ||b) && c, SomeBoolMethod() };
Si planea solo establecer algunos de los valores en la matriz, pero desea obtener el valor predeterminado (personalizado) la mayor parte del tiempo, podría intentar algo como esto:
public class SparseArray<T>
{
private Dictionary<int, T> values = new Dictionary<int, T>();
private T defaultValue;
public SparseArray(T defaultValue)
{
this.defaultValue = defaultValue;
}
public T this [int index]
{
set { values[index] = value; }
get { return values.ContainsKey(index) ? values[index] ? defaultValue; }
}
}
Probablemente necesites implementar otras interfaces para que sea útil, como las de la array .
Si puede invertir su lógica, puede usar el método Array.Clear()
para establecer la matriz booleana en falso.
int upperLimit = 21;
double optimizeMe = Math.Sqrt(upperLimit);
bool[] seiveContainer = new bool[upperLimit];
Array.Clear(seiveContainer, 0, upperLimit);
Si su matriz es tan grande, debería usar BitArray. Utiliza 1 bit para cada bool en lugar de un byte (como en una matriz de bools), también puedes establecer todos los bits en true con los operadores de bit. O simplemente inicializa en verdadero. Si solo necesitas hacerlo una vez, solo te costará más.
System.Collections.BitArray falses = new System.Collections.BitArray(100000, false);
System.Collections.BitArray trues = new System.Collections.BitArray(100000, true);
// Now both contain only true values.
falses.And(trues);
esto también funciona ... pero podría ser innecesario
bool[] abValues = new bool[1000];
abValues = abValues.Select( n => n = true ).ToArray<bool>();
Boolean[] data = new Boolean[25];
new Action<Boolean[]>((p) => { BitArray seed = new BitArray(p.Length, true); seed.CopyTo(p, 0); }).Invoke(data);
Enumerable.Repeat(true, 1000000).ToArray();