Я не знаю, разрешено ли это правилами синтаксиса OCAML или нет, но давайте добавим немного дополнительного пробела, чтобы сделать его более понятным:
let split l n =
let f x ( l1, l2 , b ) =
if x = n then ( [], x::(l1@l2), true )
else if b then (x::l1, l2 , b ) (* b is true *)
else ( l1, x:: l2 , b ) (* b is false *)
in let (l1, l2, b) = foldr f ( [], [] , false) l
in (l1, l2) ;;
foldr
в псевдокоде
foldr f z [x1 ; x2 ; ... ; xn1 ; xn ]
=
x1 -f- (x2 -f- (... -f- (xn1 -f- (xn -f- z))...))
, где a -f- b
обозначает простое приложение f a b
, просто написанный инфикс для удобства. Другими словами,
foldr f z [x1 ; x2 ; ... ; xn1 ; xn] (* x_{n-1} *)
=
f x1 (foldr f z [x2 ; ... ; xn1 ; xn]) (* x_{n-1} *)
, тогда как
foldr f z [] = z
Таким образом, вышеприведенное фактически эквивалентно
= let t1 = f xn z
in let t2 = f xn1 t1 (* x_{n-1} *)
in ....
in let tn1 = f x2 tn2 (* t_{n-2} *)
in let tn = f x1 tn1 (* t_{n-1} *)
in tn
Теперь вы сможете увидеть, что это делает работать с элементами списка справа налево, передавая промежуточные результаты последующим применениям f
слева.
Теперь вы также сможете написать это недостающее определение foldr
самостоятельно.
Так что, если мы подставим указанное вами c определение f
, как оно будет работать для списка, например, трех элементов [x1; x2; x3]
, где x2 = n
, эквивалентно
let (l1, l2, b ) = ( [], [] , false)
in let (l1_3, l2_3, b_3) = ( l1, x3::l2 , b )
in let (l1_2, l2_2, b_2) = ( [], x2::(l1_3@l2_3) , true )
in let (l1_1, l2_1, b_1) = (x1::l1_2, l2_2 , b_2 )
in (l1_1, l2_1)
, т.е.
let (l1, l2, b ) = ( [], [] , false)
in let (l1_3, l2_3, b_3) = ( [], x3::[] , false)
in let (l1_2, l2_2, b_2) = ( [], x2::([]@[x3]) , true )
in let (l1_1, l2_1, b_1) = (x1::[], [x2 ; x3] , true )
in ([x1], [x2; x3])
Таким образом, результирующие списки строятся сзади.
Передаваемые логические флаги позволяют функции правильно обрабатывать случаи, когда в списке более одного элемента, равного n
. Тот же эффект может быть достигнут без каких-либо флагов, с небольшим шагом последующей обработки, например
let split l n =
let f x ( l1, l2 ) =
if x = n then ( [], x::(l1@l2))
else (x::l1, l2 )
in match foldr f ( [], [] ) l with
| (l1, []) -> ([], l1)
| (l1, l2) -> (l1, l2) ;;
(если это не правильный код OCaml, тогда примите его как псевдокод).