Jaka byłaby prawidłowa konwersja poniższej instrukcji map na pętlę for? map(lambda x: map(lambda y: map(lambda z: x*y*z, [5,6]), [3,4]), [1,2]) # [[[15, 18], [20, 24]], [[30, 36], [40, 48]]...

1
David542 25 czerwiec 2021, 02:36

4 odpowiedzi

Najlepsza odpowiedź

Nie jestem pewien, którego REPL używasz, ale map zwraca iterator w Pythonie 3.

x = map(lambda x: map(lambda y: map(lambda z: x*y*z, [5,6]), [3,4]), [1,2])

print(x)
<map object at 0x7fd6f70d1670>

Możesz użyć list, aby przekonwertować iterator na listę -

print(list(x))
[<map object at 0x7fd6f7034f70>, <map object at 0x7fd6f70837f0>]

Jak widać, zagnieżdżone wywołania map również wytwarzają iteratory, więc musiałbyś wywołać list na zagnieżdżonych elementach, aby uzyskać rzeczywisty wynik w swoim pytaniu

x = list(map(lambda x: list(map(lambda y: list(map(lambda z: x*y*z, [5,6])), [3,4])), [1,2]))

print(x)
[[[15, 18], [20, 24]], [[30, 36], [40, 48]]]

Możesz napisać nested_for, który przyjmuje proc i zmienną liczbę iterowalnych, its -

def nested_for(proc, *its):
  def loop (acc, its):
    if not its:
      yield proc(*acc)
    else:
      for x in its[0]:
        yield list(loop((*acc, x), its[1:]))
  yield from loop(tuple(), its)
out = list(nested_for(lambda x,y,z: x*y*z, [1,2], [3,4], [5,6]))
print(out)
[[[[15], [18]], [[20], [24]]], [[[30], [36]], [[40], [48]]]]

Ta implementacja generuje jeden dodatkowy poziom zagnieżdżenia [...]. Jeśli jest to niepożądane, możesz uczynić loop nieco bardziej wyrafinowanym -

def nested_for(proc, *its):
  def loop (acc, it = None, *its):
    if not it:
      yield proc(*acc)
    elif not its:
      for v in it:
        yield proc(*acc, v) # don't nest deepest iteration
    else:
      for v in it:
        yield list(loop((*acc, v), *its))
  yield from loop(tuple(), *its)
out = list(nested_for(lambda x,y,z: x*y*z, [1,2], [3,4], [5,6]))
print(out)
[[[15, 18], [20, 24]], [[30, 36], [40, 48]]]

Widząc, że jesteś znajomy z curry, możesz uprościć nested_for, sprawiając, że akceptuje curry proc -

def nested_for (proc, it = None, *its):
  if not it:
    return
  elif not its:
    for v in it:
      yield proc(v)
  else:
    for v in it:
      yield list(nested_for(proc(v), *its))
out = list(nested_for(lambda x: lambda y: lambda z: x*y*z, [1,2], [3,4], [5,6]))
print(out)
[[[15, 18], [20, 24]], [[30, 36], [40, 48]]]

Poza tym tak naprawdę nie wiem, na jakie pytanie próbujesz odpowiedzieć. Jak wskazuje Prune, już zidentyfikowałeś odpowiednik for i wymieniłeś formy rozumienia ze zrozumieniem.

1
4 revs 25 czerwiec 2021, 21:10

Możesz użyć rekurencji:

def nest_op(d, p = 1):
   return [nest_op(d[:-1], p*i) if d[:-1] else p*i for i in d[-1]]

print(nest_op([[5,6], [3,4], [1,2]]))

Wynik:

[[[15, 18], [20, 24]], [[30, 36], [40, 48]]]
0
Ajax1234 25 czerwiec 2021, 02:39

Ty piszesz

„wydaje się, że tracisz całe zagnieżdżenie i zamiast tego wykonujesz płaskie mapowanie”.

I to jest właśnie odpowiedź na Twoje ostatnie zamknięte pytanie dotyczące kodu Scheme przetłumaczonego na Pythona.

(apply append (map foo xs)) == (flatmap foo xs) i flatmap to sposób tłumaczenia / implementacji zagnieżdżonych pętli for.

Żeby było jasne: jest zagnieżdżony w sensie bycia [result(x,y) for x in xs for y in ys]. zagnieżdżone rozumienie listy w tym pytaniu, [[result for y in ys] for x in xs] nie jest zagnieżdżonymi pętlami, to jedna pętla (for x in xs), która daje całą wewnętrzną wartość listu składanego ([result for y in ys]) jako jego wynik dla każdego x w xs.

Pętle zagnieżdżone to połączone pętle, które dają wynik jako jedną sekwencję, tylko z najgłębszej pętli. aby emulować to za pomocą list, wykonuje się flatmap, aby osiągnąć ten sam efekt.

Więc zagnieżdżone pętle są -- w pseudokodzie, --

for x in xs:
   for y in ys:
      yield result(x, y)

= [result(x, y) for x in xs for y in ys]

= (flatMapOn xs (lambda (x) =>
    (flatMapOn ys (lambda (y) => (list (result x y))))))

= (flatMapOn xs (lambda (x) =>
        (mapOn ys (lambda (y) =>       (result x y) ))))

A pętla „podobna do mapy”, o którą pytasz, to

for x in xs: 
    yield (list (for y in ys: 
                   yield result(x,y) ))

= [[result(x, y) for y in ys] for x in xs]

= (mapOn xs (lambda (x) =>
    (mapOn ys (lambda (y) => (result x y)))))

NB jest to yield, a nie yield from, w zewnętrznej pętli tutaj.

W Racket obserwujemy

> (define (flatMapOn xs foo) (flatmap foo xs))

> (define (mapOn xs foo) (map foo xs))

> (display (flatMapOn (list 1 2 3) (lambda (x) 
     (flatMapOn (list x (+ x 10) (+ x 100)) (lambda (y) 
        (list (cons x y)))))))
((1 . 1) (1 . 11) (1 . 101) (2 . 2) (2 . 12) (2 . 102) 
 (3 . 3) (3 . 13) (3 . 103))

> (display (flatMapOn (list 1 2 3) (lambda (x) 
     (mapOn (list x (+ x 10) (+ x 100)) (lambda (y) 
              (cons x y))))))
((1 . 1) (1 . 11) (1 . 101) (2 . 2) (2 . 12) (2 . 102) 
 (3 . 3) (3 . 13) (3 . 103))

> (display (mapOn (list 1 2 3) (lambda (x) 
     (mapOn (list x (+ x 10) (+ x 100)) (lambda (y) 
              (cons x y))))))
( ((1 . 1) (1 . 11) (1 . 101)) 
  ((2 . 2) (2 . 12) (2 . 102)) 
  ((3 . 3) (3 . 13) (3 . 103)) )
1
Will Ness 26 czerwiec 2021, 16:43

Twoja pierwsza próba jest rzeczywiście odpowiednią transformacją do zagnieżdżonych pętli for. Twoja ostatnia próba to odpowiednia transformacja do zagnieżdżonej listy zrozumienia.

Nie jestem pewien, dlaczego uważasz to za niezadowalające; rozwijanie pętli to często bałagan. Właśnie dostarczyłeś piękny podkład w równoważnych formach. :-)

3
Prune 24 czerwiec 2021, 23:51