문제

Python의 목록에 대해 패턴 일치를 수행하고 싶습니다.예를 들어 Haskell에서는 다음과 같은 작업을 수행할 수 있습니다.

fun (head : rest) = ...

그래서 목록을 전달할 때, head 첫 번째 요소가 될 것이며, rest 후행 요소가 됩니다.

마찬가지로 Python에서는 튜플을 자동으로 압축해제할 수 있습니다.

(var1, var2) = func_that_returns_a_tuple()

Python의 목록과 비슷한 작업을 수행하고 싶습니다.지금은 목록을 반환하는 함수와 다음을 수행하는 코드 덩어리가 있습니다.

ls = my_func()
(head, rest) = (ls[0], ls[1:])

나는 파이썬에서 두 줄이 아닌 한 줄로 그것을 어떻게든 할 수 있을지 궁금했습니다.

도움이 되었습니까?

해결책

내가 아는 한 다른 기능을 도입하지 않고 현재 파이썬에서 1 라이너로 만들 수있는 방법이 없다.

split_list = lambda lst: (lst[0], lst[1:])
head, rest = split_list(my_func())

그러나 Python 3.0에서 다양한 인수 서명 및 인수 풀기에 사용되는 특수 구문은 이러한 유형의 일반 서열 풀기에도 사용할 수있게되므로 3.0에서는 다음을 쓸 수 있습니다.

head, *rest = my_func()

보다 PEP 3132 자세한 내용은.

다른 팁

우선, 기능적 언어의 "패턴 일치"와 언급한 튜플에 대한 할당은 실제로 그다지 유사하지 않습니다.함수형 언어에서 패턴은 함수의 부분 정의를 제공하는 데 사용됩니다.그래서 f (x : s) = e 논쟁의 머리와 꼬리를 취한다는 의미는 아닙니다. f 그리고 돌아오다 e 그것들을 사용하지만 그것은 만약에 의 주장 f 형태이다 x : s (일부 x 그리고 s), 그 다음에 f (x : s) 동일하다 e.

Python의 할당은 다중 할당과 비슷합니다(원래 의도는 그런 것 같습니다).예를 들어 다음과 같이 씁니다. x, y = y, x 값을 교환하려면 x 그리고 y 임시 변수가 필요하지 않습니다(간단한 할당문을 사용하는 것처럼).이는 기본적으로 "동시" 실행에 대한 약칭이므로 패턴 일치와는 거의 관련이 없습니다. x = y 그리고 y = x.파이썬은 쉼표로 구분된 목록 대신 임의의 시퀀스를 허용하지만 이 패턴 일치를 호출하는 것은 제안하지 않습니다.패턴 일치를 사용하면 어떤 항목이 패턴과 일치하는지 여부를 확인할 수 있습니다.Python 할당에서는 양쪽의 시퀀스가 ​​동일한지 확인해야 합니다.

원하는 것을 수행하려면 일반적으로 (기능적 언어에서도) 보조 기능(다른 사람이 언급한 대로) 또는 다음과 유사한 기능을 사용합니다. let 또는 where 구문(익명 함수를 사용하는 것으로 간주할 수 있음)예를 들어:

(head, tail) = (x[0], x[1:]) where x = my_func()

또는 실제 Python에서는 다음과 같이 합니다.

(head, tail) = (lambda x: (x[0], x[1:]))(my_func())

이것은 당신이 원했던 단일 라이너라는 점을 제외하면 보조 기능을 가진 다른 사람들이 제공한 솔루션과 본질적으로 동일합니다.그러나 별도의 기능보다 반드시 더 나은 것은 아닙니다.

(제 답변이 좀 과했다면 죄송합니다.다만 구분을 명확하게 하는 것이 중요하다고 생각합니다.)

그것은 매우 '순수한 기능적'접근법이며, 따라서 Haskell의 합리적인 관용구이지만 아마도 Python에 적합하지는 않습니다. 파이썬은 매우 제한된 개념을 가지고 있습니다 패턴 이런 식으로 - 그리고 나는 당신이 그러한 종류의 구성을 구현하기 위해 다소 단단한 유형 시스템이 필요할 것이라고 생각합니다 (Erlang 버프는 여기에 동의하지 않도록 초대되었습니다).

당신이 가진 것은 아마도 당신이 그 관용구에 도달 할 것보다 가깝지만, 목록의 꼬리가있는 함수를 재귀 적으로 호출하는 대신 목록 이해력이나 명령적인 접근법을 사용하는 것이 좋습니다.

늘 그랬듯이 정해진 몇 번이나 ~ 전에, 파이썬은 실제로 기능적 언어가 아닙니다. 그것은 단지 FP 세계에서 아이디어를 빌려줍니다. 본질적으로는 아닙니다 꼬리 재귀 기능적 언어의 아키텍처에 포함 된 방식으로 많은 스택 공간을 사용하지 않고 큰 데이터 세트에서 이러한 종류의 재귀 작업을 수행하는 데 어려움이 있습니다.

연장 풀기는 3.0에 도입되었습니다http://www.python.org/dev/peps/pep-3132/

Haskell 또는 ML과 달리 Python에는 구조 패턴 매칭이 내장되어 있지 않습니다. 패턴 일치를 수행하는 가장 피스닉 방법은 Try-Xcept 블록입니다.

def recursive_sum(x):
    try:
        head, tail = x[0], x[1:]
        return head + recursive-sum(tail)
    except IndexError:  # empty list: [][0] raises IndexError
        return 0

이것은 슬라이스 인덱싱이있는 객체에서만 작동합니다. 또한 기능이 복잡해지면 신체의 무언가 ~ 후에 그만큼 head, tail 라인은 인덱스 테러를 올릴 수있어 미묘한 버그로 이어질 수 있습니다. 그러나 이것은 다음과 같은 작업을 수행 할 수 있습니다.

for frob in eggs.frob_list:
    try:
        frob.spam += 1
    except AttributeError:
        eggs.no_spam_count += 1

파이썬에서, 꼬리 재귀는 일반적으로 축합기가있는 루프로 더 잘 구현됩니다.

def iterative_sum(x):
    ret_val = 0
    for i in x:
        ret_val += i
    return ret_val

이것은 시간의 99%를 수행하는 명백하고 올바른 방법입니다. 읽는 것이 더 명확 할뿐만 아니라 더 빠르며 목록 이외의 것들 (예 : 세트)에서 작동합니다. 거기에서 기다리는 예외가 있으면 함수가 행복하게 실패하여 체인 위로 전달됩니다.

나는 일하고있다 pyfpm, 스칼라와 같은 구문과 파이썬에서 패턴 일치하는 라이브러리. 이를 사용하여 다음과 같은 개체를 풀 수 있습니다.

from pyfpm import Unpacker

unpacker = Unpacker()

unpacker('head :: tail') << (1, 2, 3)

unpacker.head # 1
unpacker.tail # (2, 3)

또는 함수의 arglist에서 :

from pyfpm import match_args

@match_args('head :: tail')
def f(head, tail):
    return (head, tail)

f(1)          # (1, ())
f(1, 2, 3, 4) # (1, (2, 3, 4))

글쎄, 왜 처음에 1 라인으로 그것을 원하십니까?

정말로 원한다면 항상 다음과 같은 트릭을 수행 할 수 있습니다.

def x(func):
  y = func()
  return y[0], y[1:]

# then, instead of calling my_func() call x(my_func)
(head, rest) = x(my_func) # that's one line :)

다른 답변에 더해, python3의 * 구문 확장을 포함하여 Python에서 동등한 헤드/테일 작업은 일반적으로 Haskell의 패턴 일치보다 효율성이 떨어집니다.

Python 목록은 벡터로 구현되므로 꼬리를 얻으려면 목록의 복사본을 가져와야 합니다.이는 목록 크기에 비해 O(n)인 반면, Haskell과 같은 연결 목록을 사용하는 구현에서는 O(1) 작업인 tail 포인터만 사용할 수 있습니다.

유일한 예외는 목록이 실제로 반환되지 않지만 반복자는 반환되는 반복자 기반 접근 방식일 수 있습니다.그러나 이는 목록이 필요한 모든 장소에 적용 가능한 것은 아닙니다(예:여러 번 반복).

예를 들어, 사이퍼의 이 접근 방식을 튜플로 변환하는 대신 반복자를 반환하도록 수정하면 이 동작이 발생합니다.또는 바이트코드에 의존하지 않는 더 간단한 2개 항목 전용 메서드는 다음과 같습니다.

def head_tail(lst):
    it = iter(list)
    yield it.next()
    yield it

>>> a, tail = head_tail([1,2,3,4,5])
>>> b, tail = head_tail(tail)
>>> a,b,tail
(1, 2, <listiterator object at 0x2b1c810>)
>>> list(tail)
[3, 4]

분명히 좋은 구문 설탕이 있는 것보다는 여전히 유틸리티 함수로 포장해야 합니다.

파이썬 요리 책에는이 작업을 수행하기 위해 재활용이있었습니다. 나는 지금 그것을 찾을 수 없지만 여기에 코드가 있습니다 (약간 수정했습니다)


def peel(iterable,result=tuple):
    '''Removes the requested items from the iterable and stores the remaining in a tuple
    >>> x,y,z=peel('test')
    >>> print repr(x),repr(y),z
    't' 'e' ('s', 't')
    '''
    def how_many_unpacked():
        import inspect,opcode
        f = inspect.currentframe().f_back.f_back
        if ord(f.f_code.co_code[f.f_lasti])==opcode.opmap['UNPACK_SEQUENCE']:
            return ord(f.f_code.co_code[f.f_lasti+1])
        raise ValueError("Must be a generator on RHS of a multiple assignment!!")
    iterator=iter(iterable)
    hasItems=True
    amountToUnpack=how_many_unpacked()-1
    next=None
    for num in xrange(amountToUnpack):
        if hasItems:        
            try:
                next = iterator.next()
            except StopIteration:
                next = None
                hasItems = False
        yield next
    if hasItems:
        yield result(iterator)
    else:
        yield None

그러나 이전 프레임을 부적합한 방식으로 인해 할당 풀기를 사용할 때만 작동합니다. 여전히 유용합니다.

특정 사용 사례 - 에뮬레이션 Haskell 's fun (head : rest) = ..., 확실한. 함수 정의는 매개 변수를 꽤 오랫동안 포장 풀기를 지원했습니다.

def my_method(head, *rest):
    # ...

Python 3.0 기준 @bpowah 말하는, Python은 과제에 대한 포장 풀기를 지원합니다.

my_list = ['alpha', 'bravo', 'charlie', 'delta', 'echo']
head, *rest = my_list
assert head == 'alpha'
assert rest == ['bravo', 'charlie', 'delta', 'echo']

별표 ( "splat")는 "끝까지"가 아니라 "반복 가능한 나머지"를 의미합니다. 다음은 잘 작동합니다.

first, *middle, last = my_list
assert first == 'alpha'
assert last == 'echo'
assert middle == ['bravo', 'charlie', 'delta']

first, *middle, last = ['alpha', 'bravo']
assert first == 'alpha'
assert last == 'bravo'
assert middle == []
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top