Форум программистов, компьютерный форум, киберфорум
Lisp
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
 
 
Рейтинг 4.64/11: Рейтинг темы: голосов - 11, средняя оценка - 4.64
 Аватар для supmener
87 / 95 / 15
Регистрация: 26.06.2013
Сообщений: 4,755

Рекурсия

27.04.2020, 07:34. Показов 2563. Ответов 23
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Здесь урок http://cybern.ru/lisp-recursive-function.html
На странице фрагмент
Теперь напишем более сложную рекурсию, реализуем функцию member, которая будет проверять, а содержится ли элемент a в списке lst:
Lisp
1
2
3
4
5
6
(defun member(lst a)
(if null(lst)
NIL
(or (equal (car lst a)) (member (cdr lst) a))
)
)
Данная функция возвращает ложь, если список пустой, а если список не пустой мы берем его первый элемент и сравниваем с элементом а или запускаем функция member на оставшимся списке(без первого элемента), если хоть кто — нибудь вернет T, то результатом будем истина.
Комментарий посетителя
Max • 3 года назад
У Вас опечатка во 2 примере в 2 и 4 строке.
Lisp
1
(if (null lst) вместо (if null(lst)
и
Lisp
1
(equal (car lst) a)
вместо
Lisp
1
(equal (car lst a))
Не знаю, как грамотно подставить, чтобы заработало (попробовал, не получилось).
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
27.04.2020, 07:34
Ответы с готовыми решениями:

Рекурсия
Общее задание: Составить рекурсивную функцию для обработки списка из любого количества атомов, которые составлены из любых знаков. Если в...

рекурсия
Прочитать из стандартного потока ввода двоичное дерево, представленное с помощью списков. Первый элемент в списке является значением узла,...

Рекурсия
Начал изучать Lisp относительно недавно, поэтому извиняюсь, если вопрос глупый. В общем, у меня есть пример функций, но почему-то в...

23
 Аватар для supmener
87 / 95 / 15
Регистрация: 26.06.2013
Сообщений: 4,755
29.04.2020, 20:48  [ТС]
Студворк — интернет-сервис помощи студентам
Непонятно, как Лисп ищет длину хвоста.
0
4528 / 3522 / 358
Регистрация: 12.03.2013
Сообщений: 6,038
29.04.2020, 21:02
Подождите с лиспом. Поймите матчасть сначала. Вы согласны, что чтобы найти длину хвоста, достаточно найти длину его собственного хвоста и прибавить 1?
0
Супер-модератор
Эксперт функциональных языков программированияЭксперт Python
 Аватар для Catstail
38209 / 21142 / 4312
Регистрация: 12.02.2012
Сообщений: 34,755
Записей в блоге: 14
30.04.2020, 07:38
Цитата Сообщение от supmener Посмотреть сообщение
Непонятно, как Лисп ищет длину хвоста
- точно так же, как длину исходного списка. Единица прибавляется к длине хвоста от хвоста. И так до тех пор, пока хвост не станет пустым. Тогда его длина=0. И к этому нулю прибавятся все единицы. Включи трассировку и убедись.

Lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
(defun длина (список)
  (if (null список) 0 (+ 1 (длина (cdr список)))))
 
==> ДЛИНА
(trace длина)
 
==> ДЛИНА
(длина '(q w e r t y))
Вход в функцию длина Аргументы: (Q W E R T Y)
  Вход в функцию длина Аргументы: (W E R T Y)
    Вход в функцию длина Аргументы: (E R T Y)
      Вход в функцию длина Аргументы: (R T Y)
        Вход в функцию длина Аргументы: (T Y)
          Вход в функцию длина Аргументы: (Y)
            Вход в функцию длина Аргументы: NIL
            Возврат из функции длина Результат: 0
          Возврат из функции длина Результат: 1
        Возврат из функции длина Результат: 2
      Возврат из функции длина Результат: 3
    Возврат из функции длина Результат: 4
  Возврат из функции длина Результат: 5
Возврат из функции длина Результат: 6
 
==> 6
1
 Аватар для supmener
87 / 95 / 15
Регистрация: 26.06.2013
Сообщений: 4,755
08.07.2020, 20:01  [ТС]
Из пособия составителя Даниленко, И. Н. Основы функционального и логического программирования: учебное пособие / И. Н. Даниленко. – Сургут: Издательство СурГУ, 2008
Появились ли ошибки при копировании текста из файла - не знаю, возможно надо сверяться с оригиналом.

Lisp
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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
Рекурсия
Рекурсия является одним из основных способов организации вычислений, связанных с обработкой списков. С учетом того, что «Lisp» – это «list processor», можно считать, что рекурсия один из важнейших способов организации вычислений в Common Lisp (и наиважнейший чистых функциональных языках).
Существуют разные способы классификации организации рекурсии, которые, надо признать, являются достаточно условными, но позволяют лучше понять природу рекурсивных вычислений и освоить технологию создания рекурсивных функций.
Можно выделить следующие признаки классификации:
 по количеству рекурсивных вызовов и их характеру;
 по способу организации рекурсивных вычислений;
 по характеру вычислительного процесса.
По количеству рекурсивных вызовов и их характеру выделяют следующие виды рекурсии.
1. Простая рекурсия. Тело рекурсивной функции f содержит один рекурсивный вызов функции f.
(defun f()((f …)).
2. Параллельная рекурсия. При параллельной рекурсии тело определения функции f содержит вызов некоторой функции g, несколько аргументов, которой является рекурсивными вызовами функции f.
(defun f()((g … (f …) (f …)))).
3. Взаимная (косвенная) рекурсия. При взаимной рекурсии тело определения функции f содержит вызов некоторой другой функции g, которая в свою очередь содержит вызов функции f.
(defun f()((f …)),
(defun f()((f …)).
4. Рекурсия высокого порядка. Рекурсия более высокого порядка возникает тогда, когда тело определения функции f содержит рекурсивный вызов функции f, аргументом которого является рекурсивный вызов f:
(defun f()(f … (f …))).
По способу организации рекурсивных вычислений можно выделить следующие виды рекурсии.
1. С одной (несколькими) терминальными ветвями и одним рекурсивным вызовом. Две (и более) терминальные ветви необходимы тогда, когда, например, возможны два (и более) принципиально отличающихся варианта решения. Если же заранее известна однозначность решения, можно обойтись и одной терминальной ветвью. Шаблон такой обобщенной рекурсивной функции приведен ниже.
(defun function(x)
   (cond (end-test-1 end-value-1)
          …
         (end-test-n end-value-n)
         (T (function reduced-x))
   )
)
Ниже приведены примеры рекурсивных функций с одной и двумя терминальными ветвями соответственно.
Пример 18. Рекурсивная функция find-first-atom осуществляет поиск первого атомарного элемента. Если считать, что атом будет всегда найден, то можно обойтись одной терминальной ветвью.
(defun find-first-atom(x)
   (cond ((atom (car x)) (car x))
         (t (find-first-atom (cdr x)))
   )
)
Пример 19. Рекурсивная функция anyoddp осуществляет поиск первого нечетного числа. Так как возможны два результата поиска, то необходимы две терминальные ветви.
(defun anyoddp(x)
   (cond ((null x) nil)
         ((oddp (сar x)) t)
         (t (anyoddp (cdr x)))
   )
)
2. Пополняющая рекурсия или рекурсия с отложенными вычислениями. Если предыдущий способ организации рекурсии позволяет просто последовательно обрабатывать некоторые данные, постепенно сокращая их объем, то в ряде случаев необходимо получить результат как объединение текущих действий и результата работы рекурсии над «уменьшенными» данными. Для случая одной терминальной ветви, хотя их может быть и больше, шаблон рекурсивной функции может выглядеть так:
(defun function (X)
   (cond (end-test end-value)
         (T (aug-fun   aug-val
                       (function reduced-x)
             )
         )
   )
)
Пример 20. Пример функции с отложенными вычислениями, высчитывающей сумму арифметического ряда от 1 до заданного n:
(defun sum(n)
   (cond ((= n 1) 1)
         (t (+ n (sum (- n 1))))
   )
)
При обработке списков очень часто в качестве накапливающей функции используется функция cons, конструирующая списки.
Пример 21. Функция – «генератор смеха»:
(defun laugh(n)
   (cond ((zerop n) nil)
         (t (cons “ha” (laugh (- n 1))))
   )
)
Очевидно, что рекурсивная функция может иметь не один, а несколько аргументов. Другим обобщением рассматриваемого вида рекурсии является рекурсия, в которой накопление происходит по условию. Шаблон такой функции может быть следующим:
(defun function (x)
   (cond (end-test end-value)
         (aug-test (aug-fun aug-val
                           (function reduced-x)
         )
         (T (function reduced-x))
   )
)
Как видно из листинга, такая рекурсия является комбинацией «простой» рекурсии с одной терминальной ветвью и накопительной рекурсии.
Пример 22. Функция, вычленяющая из списка символы:
(defun extract-symbols(x)
   (cond ((null x) nil)
         ((symbolp (car x))
             (cons (car x)
                   (extract-symbols (cdr x))
             )
         )
         (t (extract-symbols (cdr x)))
   )
)
Использование рекурсии с накоплением имеет один существенный недостаток – выполняющая среда должна поддерживать необходимую вложенность вызовов и хранить стеки. В тоже время, если рекурсивный вызов функции является «последним действием», и конечный результат работы всей функции определяется только им, то можно не сохранять стеки предыдущих рекурсивных вызовов. Такую рекурсию принято называть хвостовой рекурсией. Таким образом, выполняющая среда может оптимизировать вычисления хвостовой рекурсии и преобразовать ее в циклические вычисления, которые, как правило, гораздо эффективнее выполняются на современных аппаратных платформах. В Common Lisp задачу построения хвостовой рекурсии можно решать с помощью введения в набор аргументов функции дополнительных аргументов, которые и будут накапливать результат. Но при этом следует помнить, что реализации функции будет усложняться, а сама функция становиться семантически менее доступной (можно сравнить реализации функций в примерах 22 и 23).
Пример 23. Функция, решающая ту же задачу, что и в примере 22, но для организации внутренней функции, которая имеет дополнительный параметр, используется специальный оператор labels. Таким образом удается с одной стороны реализовать хвостовую рекурсию, а с другой – оставить количество аргументов целевой функции соответствующим ее назначению.
(defun extract-symbols(x) (labels 
  ((inner(out in) (cond 
     ((null in) out) 
     ((symbolp (car in)) 
      (inner (append out (list (car in))) (cdr in)))
     (t (inner out (cdr in)))  
   )))
   (inner nil x)
   )
)
3. CAR/CDR рекурсия. При обработке многоуровневых списков часто возникает задача полного обхода списка на всех уровнях. В этом случае на помощь приходит так называемая CAR/CDR рекурсия или рекурсия по дереву. Ее принцип легко понять, если принять во внимание тот факт, внутренняя реализация списков основана на списочных ячейках и, фактически, многоуровневые списки являются бинарными деревьями. Термин «CAR/CDR рекурсия» считается традиционным именно в контексте программирования на языке Lisp и его диалектах. По сути, такая рекурсия является комбинацией параллельной рекурсии и рекурсии с накоплением
(defun func (X)
   (cond (end-test-1 end-value-1)
         (end-test-2 end-value-2)
         (T (combiner (func (CAR X))
                      (func (CDR X))
            )
         )
   )
)
Пример 22. Функция, осуществляющая поиск числа в  многоуровневом списке:
(defun find-number (x)
   (cond ((numberp x) x)
         ((atom x) nil)
         (t (or (find-number (car x))
                (find-number (cdr x)))
         )
   )
)
Другим примером функции, использующей CAR/CDR рекурсию, является функция, представленная в примере 23.
Пример 23. Функция, вычленяющая символы из многоуровневого списка:
(defun extract-symbols (x)
   (cond ((null x) nil)
         ((symbolp (car x)) (cons (car x)
                            (extract-symbols (cdr x))
             )
         )
         ((listp (car x))
             (cons (extract-symbols (car x))
                   (extract-symbols (cdr x))
             )
         )
         (t (extract-symbols (cdr x)))
   )
)
В заключение разговора о рекурсии можно привести пример рекурсивных функций, первая из которых осуществляет переворот списка (пример 24), а вторая – вспомогательная – реализует добавление элемента в конец списка (пример 25).
Пример 24.
(defun rev(ls) (cond ((null (cdr ls)) ls) 
            (t  (add (rev (cdr ls)) (car ls)))
    )
)
Пример 25.
(defun add(a b)(cond ((null a) (cons b nil))
                     (t (cons (car a) (add (cdr a) b)))
     )
)
По характеру вычислительного процесса, рекурсивные вычисления могут быть итерационными (обычная хвостовая рекурсия, простая рекурсия), рекурсивными (параллельная рекурсия), каскадными (рекурсия высокого порядка).
Функции высокого порядка
Рассмотренные способы построения рекурсивных функций позволяют строить достаточно сложные функции, обладающие необходимой функциональностью. С другой стороны, оказывается, что эти функции достаточно типичны и по своей структуре соответствуют некоторым шаблонам. Рассмотрим реализацию двух функций: первая формирует список из первых элементов подсписков, являющихся элементами полученного в качестве аргумента двухуровневого списка (пример 26), вторая – инкрементирует значения всех элементов (чисел) одноуровневого списка (пример 27).
Пример 26.
(defun 1st(x)
   (cond (x (cons (car (car x))(1st (cdr x)))))
)
Пример 27.
(defun inc(x)
   (cond (x (cons (1+  (car x)) (inc (cdr x)))))
)
Очевидно, что эти функции отличаются только способом накопления результатов, поэтому потенциально можно объединить реализацию этих функций в одну, которая в качестве аргументов будет получать список и функциональный объект – функцию или лексическое замыкание. Функцию, которая в качестве аргумента получает функциональный объект, называют функционалом. Функцию, которая возвращает функциональный объект и/или получает функциональный объект в качестве аргумента называют функцией высокого порядка [6].
Специальный оператор function блокирует вычисления, образуя так называемое лексическое замыкание. Лексическое замыкание – это определение функции (лямбда-выражение) и контекст определения функции. Если блокируемая функция не содержит свободных переменных, то функциональная блокировка не отличается от обычной, выполняемой с помощью функции quote. Для выполнения функциональной блокировки можно использовать синоним #’. Вызвать функциональный объект (заблокированную функцию или замыкание) можно с помощью, так называемых, применяющих функционалов funcall и apply (приложение А).
Пример 28.
(funcall #’exp 0) => 1.0  ; попытки вызвать
; (eval exp 0) или (eval #’exp 0) ошибочны
(apply #’(lambda(x)(* x 2)) 2) => 4
Пример 29. Функция x+1 строит функцию-генератор последовательности целых чисел.
(defun x+1()(let ((x 1))
      #’(lambda()(setq x (+ x 1))))) => X+1
(setq x (x+1)) => ;связывание с x замыкания X+1
#<interpreted closure (LAMBDA NIL (SETQ X (+ X 1)))>
(setq y (x+1)) => ;связывание с y замыкания X+1
#<interpreted closure (LAMBDA NIL (SETQ X (+ X 1)))>
(funcall x) => 2 ;вызов функции
(funcall x) => 3 ;еще один вызов функции
(funcall y) => 2 ;а связи этой функции – другие!
Из примера 29 следует, что в замыкании сохраняются связи с локальным контекстом и, фактически, экземпляры замыканий можно рассматривать как объекты в терминах объектно-ориентированного подхода.
С учетом рассмотренных возможностей языка Common Lisp можно вернуться к примерам 27 и 28. Предложенное ранее обобщение этих функций и его применение показаны в примере 30.
Пример 30. Простой отображающий функционал.
(defun map-el(x f)
  (cond (x (cons (funcall f (car x))
                 (map-el (cdr x) f)))
  )
) => map-el
(map-el ‘(1 2 3) #’1+) => (2 3 4)
(map-el ‘(-1 -3 4 -5) #’abs) => (1 3 4 5)
Как следует из последнего примера, при построении функции map-el использовались: композиция функций, рекурсия и возможность использовать функции как объекты первого класса, т. е. ключевые возможности функционального подхода. Среди стандартных функций Common Lisp существует большое количество функционалов: группа отображающих функционалов (map, mapcar, maplist и др.), функционалы, которые получают в качестве параметра функцию-предикат (sort, remove, remove-if, subst, subst-if и др.), но это не лишает разработчика определять свои собственные функционалы.
Построенный функционал является примером функций высших порядков – функций, которые получают в качестве аргумента функцию и/или возвращают новую функцию. Можно показать, что не только передача аргументов-функций, но и возвращение функции как результата может играть большую роль в построении программ. Эффективность и элегантность функционального подхода можно показать на примере задачи построения распознавателя простого контекстно-свободного языка (этот пример рассматривается в [2, 5], однако в [5] он не снабжен примерами реализации на Common Lisp, а в [2] допущено много опечаток). Контекстно-свободным является язык, синтаксис которого может быть описан множеством правил, обычно называемых продукциями. В данном случае пусть язык задан следующими правилами:
S ::= BA | AB | BAB,  A ::= a | aA, B ::= b | bB.
В данной грамматике терминальными символами являются a и b. Таким образом, следующие цепочки символов будут принадлежать грамматике языка:
ba, ab, bab, bbba, bbaabb, baaabbb,
а приведенные ниже – нет:
aa, bb, aaa, bbb, abbaabb, baaabbba.
Можно считать, что распознаваемые строки представляются списками элементов a и b. Пусть определены функции одного аргумента is-a и is-b, которые возвращают значение T тогда, когда переданный им аргумент является списком и содержит один допустимый символ:
(defun is-a(x) (if (eq (car x) 'a) (null (cdr x))))
(defun is-b(x) (if (eq (car x) 'b) (null (cdr x))))
Для решения задачи выбранным способом потребуется определить несколько вспомогательных функций. Первой из таких функций будет функция, результатом работы которой является предикат, возвращающий значение T, если хотя бы один аргументов p или q имеют такое значение.
(defun is-alt(p q)
  #'(lambda (x) (or (funcall p x) (funcall q x)))
)
Следующая функция более сложная, она должна генерировать предикат, способный определять, можно ли разделить список x на две части так, чтобы первая удовлетворяла p, а вторая – q.
(defun is-chain(p q)
  #'(lambda (x)
    (cond 
    ((null x) (and (funcall p nil) (funcall q nil)))
    ((and (funcall p nil) (funcall q x)) t)
    (t (funcall (is-chain
                   #'(lambda(y)
                      (funcall p (cons (car x) y)))q
                 )     
                 (cdr x)
               )
            )
        )
    )
)
Используя эту функцию можно строить гораздо более мощные предикаты: ниже приведены два предиката, построенные на основе is-chain и способные распознавать цепочки одинаковых символов a и b соответственно.
(defun is-a-gr(x)
  (funcall (is-alt #'is-a 
                  (is-chain #'is-a #'is-a-gr)) x)
)
(defun is-b-gr(x)
  (funcall (is-alt #'is-b 
                  (is-chain #'is-b #'is-b-gr)) x)
)
Эти рекурсивные функции позволяют определять функции-распознаватели слов, в соответствии с их грамматикой. Причем определение функции при этом становится очень близко по форме правилам, задающим грамматику языка. Например, функция, распознающая цепочку из двух символов a и b, может быть реализована так
(defun is-ab(x)(funcall (is-chain #'is-a #'is-b) x))
Реализация функции, распознающей цепочки, принадлежащие языку с описанной ранее грамматикой, будет более сложной, но, тем не менее, остается достаточно прозрачной
(defun is-syllable(x)
  (funcall 
     (is-alt 
        (is-chain #'is-b-gr #'is-a-gr)
        (is-alt 
            (is-chain #'is-a-gr #'is-b-gr)
            (is-chain #'is-b-gr 
                      (is-chain #'is-a-gr #'is-b-gr)
            )
         )
      )
     x 
  )
)
Функция, распознающая цепочки вида (b a) и (b b).
(defun is-simple(x) 
  (funcall 
     (is-chain #'is-b-gr (is-alt #'is-b-gr #'is-a-gr)) 
     x
  )
)
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
08.07.2020, 20:01

Рекурсия
Есть список элементов. Найти среднее значение элементов списка, разбить на 2 части слева меньшие среднего значения, справа большие среднего...

рекурсия
Помогите. на Лисп описать функцию одного аргумента L, которая принимает значение T, если условие выполняется: (n - 1) -й элемент L...

Рекурсия
Привести пример задачи из реальной предметной области, которую можно решить с помощью механизма рекурсии. Описать алгоритм решения задачи...

Рекурсия
Помогите пожалуйста с заданием: Связать базу данных (потомок, родители) с глобальной переменной FAMILY: (setf family '((colin...

Функции Рекурсия
Помогите, пожалуйста, с рекурсивной функцией, никак врубиться не могу. Написать функцию, аргументом которой является многоуровневый...


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

Или воспользуйтесь поиском по форуму:
24
Ответ Создать тему
Новые блоги и статьи
Эксперимент с картинкой-обложкой на CodinGame
Adler 10.07.2026
Решил на днях провести эксперимент. У меня есть аккаунт на CodinGame, ТОП-3 по РФ среди 2200 участников (в разделе соревнования по программированию ИИ-ботов). Там есть обложка, куда можно влепить. . .
После бана на Пикабу не работает авторизация аккаунта с Яндекса.
Hrethgir 09.07.2026
В частности https:/ / auto. ru/ logbook/ zapret-na-vozhdenie-posle-65-let_1cf28789-07b4-4c75-a2f7-4cd4edf9deeb тут хотел оставить комментарий такой Зрите в корень - монополизация на разработку программ. . .
GitDOS: Когда я решил скрестить MS-DOS, GitHub и Claude
MaGz GoLd 08.07.2026
Привет, Киберфорум! Хочу поделиться своим проектом, который, надеюсь, вас заинтересует. GitDOS — это веб-приложение, которое позволяет запускать DOS в браузере с тремя крутыми фичами: диски живут в. . .
Учебный мини "цифровой двойник" = (Сервер = Java + Flask_питон )+(3D = Гугловскаая 4T nvideo + google colab) +( WMS = Odoo на Docker)
anaschu 07.07.2026
Увидел в требованиях к вакансии создателя имитационных моделей связку (REst + Api + WMS + Anylogic + питон + 3D Nvideo), и решил посмотреть, что это такое. Из этого всего я работал более менее. . .
Море в объективе
kumehtar 06.07.2026
Хотел написать много проникновенных слов, но передумал. Оно само - говорит. Кто слышит тот слышит.
Doom для терминала без стрельбы и монстров. 3D Raycasting на ascii.
dcc0 05.07.2026
Попросил нейронную сеть deepai. org написать рейкастинг 3D с библиотекой ncurses для Linux. Чтобы можно было ходить на стрелочки. Чтобы стены были отрисованы символами. Справилась. Первый вариант. . .
Установка статуса документа по условию
Maks 05.07.2026
Алгоритм из решения ниже реализован на нетиповом документе "НарядПутевка" разработанного в КА2. Задача: в табличной части "Материалы" документа при записи автоматически устанавливать статус. . .
Сезонность и суточность закисления почв
anaschu 04.07.2026
200 часов это все равно моловато. Есть ситуации, но нестандартные, когда смена происходит за 5 лет. Но обычно это 50 лет и более. Наверное, закисление почвы происходит сезонно в средней. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru