Форум программистов, компьютерный форум, киберфорум
C# для начинающих
Войти
Регистрация
Восстановить пароль
Карта форума Темы раздела Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.80/40: Рейтинг темы: голосов - 40, средняя оценка - 4.80
34 / 35 / 12
Регистрация: 04.03.2017
Сообщений: 444
1

Рекурсивный перебор элементов массива любой размерности

31.03.2017, 17:59. Показов 7915. Ответов 9
Метки нет (Все метки)

Author24 — интернет-сервис помощи студентам
Так долго вожусь и все равно ничего :
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public static void _ForEach<T>(this Array a , Func<T,T> act){
            int[] indices = new int[a.Rank];
            a._For(i =>a.SetValue( act((T)a.GetValue(i)) , i),ref indices , 0);
        }
        public static void _For(this Array a , Action<int[]> act ,ref int[] indices, int j = 0 ){
            act(indices);
            try{indices[j]++;} catch(IndexOutOfRangeException) { //Этот алгоритм пропускает 1 и 1 2 и 2 ,я это восполняю
                for (UInt32 i = 0; i < a.Rank; i++) {
                    for ( UInt32 i_ = 0; i_ < indices.Rank ;i_++) indices[i_] = (int)i;
                    act(indices);
                }
                return;
            }
            if (indices[j] < a.GetLength(j)-1) a._For(act,ref indices,j);
            else a._For(act,ref indices,++j);
            
        }
Очень уродливый код и безысходность , спасайте.
0
Лучшие ответы (1)
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
31.03.2017, 17:59
Ответы с готовыми решениями:

Рекурсивный перебор. Вылет за границы массива
ка, состоящая из попарно различных символов (буквы латинского алфавита и цифры). Требуется вывести...

Рекурсивный перебор директорий сервера
Всем доброго времени суток уважаемые форумчане, недавно начал изучение языка C# Вот уже несколько...

Перебор элементов массива
Всем привет. Допустим есть двухмерный массив. В чем суть допустим 0 это пустота с ней ничего не...

Рекурсивный подсчет суммы всех элементов массива
Реализуйте рекурсивный подсчет суммы всех элементов масси- ва. Сумма элементов массива считается...

9
6219 / 2467 / 725
Регистрация: 11.04.2015
Сообщений: 3,987
Записей в блоге: 43
31.03.2017, 20:21 2
RaevskiAnatoly, вообще, задачу можно было бы и словами описать, а так не очень понятно, что надо. Если предположить, что форич делает то, что обычно делают форичи, то можно как-то так.
C#
1
2
3
4
5
6
        static void ForEach<T>(T[] a, Action<T> act, int index = 0)
        {
            if (a.Length == 0) return;
            act(a[index]);
            if (index < a.Length - 1) ForEach(a, act, index + 1);
        }
Вызывать так к примеру
C#
1
            ForEach(new int[] { 1, 2, 3, 4, 5 }, Console.WriteLine);
0
138 / 138 / 53
Регистрация: 14.06.2016
Сообщений: 467
01.04.2017, 08:25 3
Цитата Сообщение от diadiavova Посмотреть сообщение
то можно как-то так
так надо же для массива любой размерности, а у вас только для одномерного.
0
Эксперт .NET
17688 / 12873 / 3366
Регистрация: 17.09.2011
Сообщений: 21,138
01.04.2017, 11:57 4
Цитата Сообщение от RaevskiAnatoly Посмотреть сообщение
Рекурсивный перебор элементов массива любой размерности
Надо просто пройтись по всем элементам и выполнить какое-то действие?
Для этого не нужна рекурсия, достаточно пройтись обычным форычем — он учитывает размерность массива.
0
6219 / 2467 / 725
Регистрация: 11.04.2015
Сообщений: 3,987
Записей в блоге: 43
01.04.2017, 14:25 5
Лучший ответ Сообщение было отмечено RaevskiAnatoly как решение

Решение

Цитата Сообщение от kolorotur Посмотреть сообщение
Надо просто пройтись по всем элементам и выполнить какое-то действие?
Для этого не нужна рекурсия, достаточно пройтись обычным форычем — он учитывает размерность массива.
Есть подозрение, что сделать это надо именно рекурсивно без использования встроенных возможностей. Иначе - почему рекурсивно, а не циклом, например?
Цитата Сообщение от jr_ Посмотреть сообщение
так надо же для массива любой размерности, а у вас только для одномерного.
Можно сделать также, только вместо прямой индексации массива создать отдельный метод, который будет вычислять по прямому индексу (одномерному) индексы для каждого измерения. Метод также можно сделать рекурсивным, и не использовать циклы. Вот я пример набросал, там правда порядок обработки может показаться несколько неожиданным, но, насколько я понимаю задачу этот вариант выполняет
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
 
            int[,,,] a =
            {
                {
                    {
                        {1,2,3 },
                        {4,5,6 },
                        {7,8,9 }
                    },
                    {
                        {10,11,12},
                        {13,14,15},
                        {16,17,18}
                    }
                },
                {
                    {
                        {19,20,21 },
                        {22,23,24 },
                        {25,26,27 }
                    },
                    {
                        {28,29,30},
                        {31,32,33},
                        {34,35,36}
                    }
                }
            };
            ForEach(a, Console.WriteLine);
            Console.ReadKey();
        }
 
        static void ForEach(Array a, Action<object> act, int index = 0)
        {
            if (a == null || index < 0 || index >= a.Length) return;
            int[] ind = new int[a.Rank];
            FillIndArray(a, index, 0, ind);
            act(a.GetValue(ind));
            if (index < a.Length - 1) ForEach(a, act, index + 1);
        }
 
 
        private static void FillIndArray(Array a, int index, int dimension, int[] result)
        {
            int d, r;
            d = Math.DivRem(index, a.GetLength(dimension), out r);
            result[dimension] = r;
            if (dimension < a.Rank - 1)
            {
                FillIndArray(a, d, dimension + 1, result);
            }
        }
    }
}
Хотя код нуждается в тестировании, поскольку ничего гарантировать не могу.
1
34 / 35 / 12
Регистрация: 04.03.2017
Сообщений: 444
01.04.2017, 17:02  [ТС] 6
Цитата Сообщение от kolorotur Посмотреть сообщение
Надо просто пройтись по всем элементам и выполнить какое-то действие?
Для этого не нужна рекурсия, достаточно пройтись обычным форычем — он учитывает размерность массива.
Действительно , главного не сказал , нужно чтобы объект передавался по ссылке в act , это я пробовал , но пришел к выводу что это невозможно.Поэтому я передаю в act объект-копию , а возвращаемое значение записываю в соответствующее место.Проблема только в том , что не получается пройтись по всему массиву запоминая позицию текущего элемента .
В названии же много сказано , не стал же бы я писать реализацию foreach неизвестно как и зачем?
По сути хочу тоже самое (foreach) , но с доступом на запись.
0
34 / 35 / 12
Регистрация: 04.03.2017
Сообщений: 444
01.04.2017, 17:37  [ТС] 7
Отличный механизм ! Хоть я его и не понял , но он работает .
Я немного подправил код под себя и :
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public delegate void ObjectOperation<T>(out T obj);
    public static class Extensions
    {
public static void ForEach<T>(this Array a, ObjectOperation<T> act , int index = 0){
            if ( index < 0 || index >= a.Length) return;
            int[] ind = new int[a.Rank];
            FillIndArray(a, index, 0, ind);
            T CurrObj = (T)a.GetValue( ind );
            act( out CurrObj );
            a.SetValue(CurrObj , ind);
            if (index < a.Length - 1) ForEach(a, act, index + 1);
        }
        
        static void FillIndArray(Array a, int index, int dimension, int[] result){
            int d, r;
            d = Math.DivRem(index, a.GetLength(dimension), out r);
            result[dimension] = r;
            if (dimension < a.Rank - 1) FillIndArray(a, d, dimension + 1, result);
        }
А использовать так:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Program
    {
        public static void Main(string[] args)
        {
            var rnd = new Random();
            int[,] m = new int[3,3];
            //Заполняем массив одним веяньем строчки , а ведь он мог быть любой размерности
            //и никаких вложенных циклов , ну разве что длинная цепочка вызовов методов)
            m.ForEach( (out int i) => i = rnd.Next() );
            //Вывод
            foreach (var el in m ) Console.WriteLine(el);
            Console.ReadKey(true);
        }
    }
Рекурсивный перебор элементов массива любой размерности
0
34 / 35 / 12
Регистрация: 04.03.2017
Сообщений: 444
01.04.2017, 18:18  [ТС] 8
Цитата Сообщение от diadiavova Посмотреть сообщение
Есть подозрение, что сделать это надо именно рекурсивно без использования встроенных возможностей.
Кстати , если есть то , чем код можно сократить и сделать его быстрей , то пожалуйста.

Не по теме:

Я просто заливаю шлако-код в свою шлако-либу , а с помощью него очень легко решать задачки олимпиадные которые я якобы решить не могу своими ручками . Ну это такое мнение сложилось когда они пишут черти-что , а я одной строчкой через "встроенные возможности".



Добавлено через 29 минут
Хотя лучше , конечно , добавить пару перегрузок.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
    public delegate void ObjectOperationRef<T>(ref T obj);
    public delegate void ObjectOperationOut<T>(out T obj);
public static class Extensions
    {
        public static void ForEach<T>(this Array a, ObjectOperationOut<T> act , int index = 0){
            if ( index < 0 || index >= a.Length) return;
            int[] ind = new int[a.Rank];
            FillIndArray(a, index, 0, ind);
            T CurrObj = (T)a.GetValue( ind );
            act( out CurrObj );
            a.SetValue(CurrObj , ind);
            if (index < a.Length - 1) ForEach(a, act, index + 1);
        }
        public static void ForEach<T>(this Array a, ObjectOperationRef<T> act , int index = 0){
            if ( index < 0 || index >= a.Length) return;
            int[] ind = new int[a.Rank];
            FillIndArray(a, index, 0, ind);
            T CurrObj = (T)a.GetValue( ind );
            act( ref CurrObj );
            a.SetValue(CurrObj , ind);
            if (index < a.Length - 1) ForEach(a, act, index + 1);
        }
        
        static void FillIndArray(Array a, int index, int dimension, int[] result){
            int d, r;
            d = Math.DivRem(index, a.GetLength(dimension), out r);
            result[dimension] = r;
            if (dimension < a.Rank - 1) FillIndArray(a, d, dimension + 1, result);
        }
    }
0
6219 / 2467 / 725
Регистрация: 11.04.2015
Сообщений: 3,987
Записей в блоге: 43
02.04.2017, 00:11 9
Цитата Сообщение от RaevskiAnatoly Посмотреть сообщение
Хоть я его и не понял
Основная функция имеет достаточно простой алгоритм, я думаю ее пояснять не нужно. Что до FillIndArray, то ее задача состоит в том, чтобы преобразовать один индекс в массив индексов. То есть, грубо говоря, если все элементы массива представить сложенными в одномерный массив, то их можно будет проиндексировать одним индексом, а функция просто вычисляет соответствующий этому индексу набору индексов многомерного массива. Реализации этой идеи могут быть разными, различия в основном в том, в каком порядке элементы многомерного массива выкладываются в одномерный массив. Как я все это реализовал будет проще понять, если вспомнить алгоритм перевода чисел в различные системы счисления. Число делится на основание системы и остатки вписываются в обратном порядке. Но там каждый разряд имеет одинаковое максимальное значение - на единицу меньше основания системы. В нашем же случае для получения очередного индекса надо делить на количество элементов в данном измерении и остаток и будет индексом для данного измерения.
Цитата Сообщение от RaevskiAnatoly Посмотреть сообщение
Кстати , если есть то , чем код можно сократить и сделать его быстрей , то пожалуйста.
Ну сократить радикально - вряд ли, что до "сделать быстрей", ну надо было изначально делать все циклами, а не рекурсией. Переписать все в виде циклов - не проблема, поскольку хвостовая рекурсия всегда может быть переписана в виде цикла(компиляторы функциональных языков делают это автоматически).
0
34 / 35 / 12
Регистрация: 04.03.2017
Сообщений: 444
02.04.2017, 09:31  [ТС] 10
Цитата Сообщение от diadiavova Посмотреть сообщение
ну надо было изначально делать все циклами, а не рекурсией
Я и делал , но работало криво , переделал под рекурсию - тоже самое , но как я и думал , смог понять в чем проблема.Мне почему-то через рекурсию прозрачней , в отладке как-то проще.

Добавлено через 45 минут
Цитата Сообщение от diadiavova Посмотреть сообщение
Переписать все в виде циклов - не проблема
Просто удивительный механизм компиляции в #Develop - это же нужно как ухитриться , чтоб перекомпилировать лишь отдельные методы!Все думаешь в чем ошибка , в чем ошибка , прошелся отладкой по всем методам - нет ошибки!
Получилось так :
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public delegate void ObjectOperationRef<T>(ref T obj);
    public delegate void ObjectOperationOut<T>(out T obj);
public static class Extensions
    {
        public static void Print ( this Array a , Action<object> i = null){
            if (i != null) foreach (var el in a) i(el);
            else foreach (var el in a) Console.WriteLine(el);
        }
        public static void ForEach<T>(this Array a, ObjectOperationOut<T> act , int index = 0){
            if ( index < 0 || index >= a.Length) return;
            int[] ind = new int[a.Rank];
            for(;index < a.Length;index++){
            FillIndArray(a, index, 0, ind);
            T CurrObj = (T)a.GetValue( ind );
            act( out CurrObj );
            a.SetValue(CurrObj , ind);
            }
        }
        public static void ForEach<T>(this Array a, ObjectOperationRef<T> act , int index = 0){
            if ( index < 0 || index >= a.Length) return;
            int[] ind = new int[a.Rank];
            for(;index < a.Length;index++){
            FillIndArray(a, index, 0, ind);
            T CurrObj = (T)a.GetValue( ind );
            act( ref CurrObj );
            a.SetValue(CurrObj , ind);
            }
        }
        static void FillIndArray(Array a, int index, int dimension, int[] result){
            for (;dimension < a.Rank;dimension++){
            int r;
            index = Math.DivRem(index, a.GetLength(dimension), out r);
            result[dimension] = r;
            }
        }
    }
0
02.04.2017, 09:31
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
02.04.2017, 09:31
Помогаю со студенческими работами здесь

Перебор одиночных элементов как массива
int a,b,c,d,c1,c3,c4,c5,c6; foreach (int g in {a,b,c,d,c1,c3,c4,c5,c6}){g=5;} Как сделать что...

Перебор элементов массива неизвестной величины
Добрый вечер. есть вот такая штука: string Numbers = str.Split('/'); Т.е. в массив...

Для указанной директории произвести рекурсивный перебор всех вложенных поддиректорий и файлов
Здравствуйте. Нужен совет по одному заданию. &quot;Необходимо для указанной директории произвести...

Из элементов массива С сформировать массив А той же размерности по правилу
3.Из элементов массива С сформировать массив А той же размерности по правилу: если номер четный, то...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
10
Ответ Создать тему
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru