2012-01-19 31 views
7

Vì vậy, tôi xanh như cỏ và lập trình học tập từ How to think like a computer scientist: Learn python 3. Tôi có thể trả lời câu hỏi (xem bên dưới) nhưng sợ tôi đang bỏ lỡ bài học.Viết chức năng tổng quát cho cả hai chuỗi và danh sách trong python

Viết một hàm (gọi tắt là insert_at_end) sẽ vượt qua (trả lại táo bạo đưa ra hai luận điểm trước đó) cho cả ba:

test(insert_at_end(5, [1, 3, 4, 6]), **[1, 3, 4, 6, 5]**) 
test(insert_at_end('x', 'abc'), **'abcx'**) 
test(insert_at_end(5, (1, 3, 4, 6)), **(1, 3, 4, 6, 5)**) 

Cuốn sách cung cấp cho gợi ý này: "Những bài tập minh họa độc đáo mà trừu tượng chuỗi nói chung, (vì cắt, lập chỉ mục và ghép nối rất chung chung), do đó, có thể viết các hàm chung hoạt động trên tất cả các loại trình tự. ".

Phiên bản này không có các giải pháp trên mạng (mà tôi có thể tìm thấy) nhưng trong tôi tìm thấy câu trả lời của một ai đó đến một phiên bản trước của văn bản (ví python 2.7) và họ đã làm điều đó theo cách này:

def encapsulate(val, seq): 
    if type(seq) == type(""): 
     return str(val) 
    if type(seq) == type([]): 
     return [val] 
    return (val,) 

def insert_at_end(val, seq): 
    return seq + encapsulate(val, seq) 

Điều này dường như giải quyết được câu hỏi bằng cách phân biệt giữa các danh sách và chuỗi ... đi ngược lại gợi ý. Vì vậy, làm thế nào về nó Có cách nào để trả lời câu hỏi (và khoảng 10 cái tương tự hơn) mà không phân biệt? tức là không sử dụng "type()"

+1

Tôi không nghĩ rằng bạn sẽ tìm hiểu bất cứ điều gì hữu ích từ việc cố gắng giải quyết vấn đề này. –

Trả lời

1

Đây là không phải là giải pháp mà là giải thích tại sao giải pháp thực sự thanh lịch không thể nhìn được.

  • + nối chuỗi, nhưng chỉ các trình tự cùng loại.
  • giá trị được chuyển làm đối số đầu tiên cho insert_at_end là 'vô hướng', vì vậy bạn phải chuyển đổi chúng thành loại thứ tự mà đối số thứ hai có.
  • để làm điều đó, bạn không thể chỉ đơn giản gọi một hàm tạo chuỗi với đối số vô hướng và tạo chuỗi một mục thuộc loại đó: tuple(1) không hoạt động.
  • str hoạt động khác với các loại chuỗi khác: tuple(["a"])("a",), list(["a"])["a"], nhưng str(["a"]))"['a']" và không "a".

này làm cho + vô dụng trong tình huống này, mặc dù bạn có thể dễ dàng xây dựng một chuỗi các loại nhất định sạch, mà không instanceof, chỉ bằng cách sử dụng type().

Bạn cũng không thể sử dụng gán phân đoạn vì chỉ có các danh sách có thể thay đổi.

Trong trường hợp này, giải pháp của @Hamish trông sạch sẽ nhất.

+0

Giải pháp của Hamish khá sạch sẽ, nhưng tôi nghĩ rằng nó sẽ rất tệ nếu bạn cố gắng chèn một chuỗi ký tự đa vào cuối danh sách các chuỗi. – Duncan

+0

@Duncan: tốt, với danh sách, bạn có thể thêm danh sách lồng nhau, nhưng không có thứ gì như chuỗi lồng nhau. Để ngăn chặn nó một cách rõ ràng, bạn cần kiểm tra rõ ràng 'str' (đánh bại mục đích) hoặc có một hệ thống kiểu tĩnh đủ mạnh để cấm các danh sách lồng nhau (có một ngôn ngữ khác với Python). – 9000

+0

@ 9000 Tuyệt vời Cảm ơn bạn, cần thiết (nhiều hơn sau đó một câu trả lời cụ thể) tôi đã tìm cách để xác nhận rằng khi bạn đặt nó "một giải pháp thanh lịch không nhìn có thể". Tôi đã lo lắng rằng tôi đã bỏ lỡ một khái niệm cơ bản mà sau này tôi có thể gây tổn thương cho tôi. –

-1

Khi đóng gói dựa vào loại, mã trực tiếp trong insert_at_end không và dựa vào + nghĩa là các thứ liên quan cho cả 3 loại, và theo nghĩa đó, phù hợp với gợi ý .

+0

Phải có câu trả lời thú vị hơn. Câu trả lời có thể có thể sử dụng loại, nhưng không phải loại cụ thể. –

+0

Bạn không thể chỉ ném mã vi phạm vào một chức năng khác và gọi đó là chiến thắng! : D – Hamish

+0

Tôi cũng muốn được xem điều đó - nhưng tôi chưa từng thấy ai đề xuất. –

2

Tôi muốn nói rằng ví dụ này là không symetric, có nghĩa là nó yêu cầu người đọc xử lý hai trường hợp khác nhau:

  • int, danh sách
  • str, str

Trong tôi ý kiến, bài tập nên yêu cầu thực hiện điều này:

  • danh sách: insert_at_end ([5], [1, 3, 4, 6])
  • str, str: insert_at_end ('x', 'abc')

Trong trường hợp này, người đọc phải chỉ làm việc với hai thông số đó sử dụng các loại trình tự và gợi ý sẽ có ý nghĩa nhiều hơn nữa .

+0

Tôi không đồng ý - đó không phải là loại chuỗi là vấn đề - đó là liệu giá trị mới có thể lặp lại hay không. – Hamish

+1

Tôi nghĩ rằng, toàn bộ vấn đề là str (hoặc unicode) làm việc khác với danh sách, tuple hoặc bất cứ điều gì. Nếu không str (['a', 'b', 'c']) sẽ tạo ra 'abc' chứ không phải '[' a ',' b ',' c '] "... –

0

Thách thức với câu hỏi này (trong Python 2.7, tôi đang thử nghiệm 3.2 ngay bây giờ để xác minh) là hai loại đầu vào có thể cho seq là không thay đổi và bạn dự kiến ​​trả về cùng loại như đã được chuyển . trong Đối với chuỗi, đây là ít hơn của một vấn đề, bởi vì bạn có thể làm điều này:

return seq + char 

Như rằng sẽ trả về một chuỗi mới đó là nối của chuỗi đầu vào và các nhân vật nối, nhưng điều đó không làm việc cho danh sách hoặc bộ dữ liệu. Bạn chỉ có thể nối một danh sách vào một danh sách hoặc một tuple vào một bộ dữ liệu. Nếu bạn muốn tránh "loại" kiểm tra, bạn có thể đạt được điều đó với một cái gì đó như thế này:

if hasattr(seq, 'append'): # List input. 
    seq.append(char) 
elif hasattr(seq, 'strip'): # String input. 
    seq = seq + char 
else: # Tuple 
    seq = seq + (char,) 

return seq 

Đó là thực sự không khác nhiều so với các loại thực sự kiểm tra, nhưng nó tránh sử dụng các chức năng type trực tiếp.

1

Vấn đề đó là một trong một danh sách dài và gợi ý áp dụng cho tất cả chúng. Tôi nghĩ rằng nó là hợp lý rằng, có viết các chức năng encapsulate mà có thể được tái sử dụng cho những thứ như insert_at_front, phần còn lại của việc thực hiện là loại bất khả tri.

Tuy nhiên, tôi nghĩ rằng một thực hiện tốt hơn encapsulate có thể là:

def encapsulate(val, seq): 
    if isinstance(seq, basestring): 
     return val 
    return type(seq)([val]) 

mà xử lý một phạm vi rộng các loại với mã ít hơn.

+0

Upside: giải pháp này hoạt động. Nhược điểm: nó không phải là một giải pháp loại-agnostic tốt đẹp mà sử dụng giao diện phổ biến của trình tự; thay vào đó, nó đặc biệt là một số lớp học phép thuật. – 9000

+1

@ 9000, dây thường cần phải được đóng gói đặc biệt, chúng là chuỗi duy nhất chỉ chứa các đối tượng cùng loại với chính chúng và làm lộn xộn rất nhiều mã được nhập bằng vịt sạch. Nếu không, nó sẽ xử lý 'list',' tuple', các lớp con của chúng và bất kỳ chuỗi nào khác theo mô hình chung mà nó có thể được xây dựng từ một danh sách. – Duncan

+0

có, chuỗi cần phải được đặc biệt-cased, và đây là sự thật nghiệt ngã mà ngăn ngừa vấn đề này từ việc có một giải pháp hoàn toàn dựa trên giao diện, độc lập, hoàn toàn dựa trên giao diện. – 9000

0

Giải pháp này vẫn yêu cầu một số mã riêng biệt cho chuỗi thay vì danh sách/bộ dữ liệu, nhưng ngắn gọn hơn và không thực hiện bất kỳ kiểm tra nào đối với các loại cụ thể.

def insert_at_end(val, seq): 
    try: 
     return seq + val 
    except TypeError: # unsupported operand type(s) for + 
     return seq + type(seq)([val]) 
+0

làm thế nào về: khẳng định insert_at_end (['val'], ['seq']) == ['seq', ['val']]? –

2

nỗ lực tốt nhất của tôi:

def insert_at_end(val, seq): 
    t = type(seq) 
    try: 
     return seq + t(val) 
    except TypeError: 
     return seq + t([val]) 

này sẽ cố gắng để tạo ra một chuỗi các type(seq) và nếu val không iterable tạo ra một danh sách và concatenates.

+0

'kiểm tra (insert_at_end ('xyz', ['abc']), ** ['abc', 'xyz'] **)' sẽ không vượt qua. – Duncan

0

Có lẽ đây là gần câu trả lời:

def genappend(x, s): 
    if isinstance(s, basestring): 
     t = s[0:0].join 
    else: 
     t = type(s) 
    lst = list(s) 
    lst.append(x) 
    return t(lst) 

print genappend(5, [1,2,3,4])  
print genappend(5, (1,2,3,4)) 
print genappend('5', '1234') 

Ngoài ra còn có thể là hoàn toàn do người dùng định nghĩa kiểu chuỗi. Họ cũng sẽ hoạt động miễn là có thể chuyển đổi đến và từ danh sách. Điều này cũng hoạt động:

print genappend('5', set('1234')) 
0

Tôi đồng ý rằng điểm là nếu item có thể lặp lại hay không.

Vì vậy, giải pháp của tôi sẽ là:

def iterate(seq, item): 
    for i in seq: 
     yield i 
    yield item 

def insert_at_end(seq, item): 
    if hasattr(item, '__iter__'): 
     return seq + item 
    else: 
     return type(seq)(iterate(seq, item)) 

Ví dụ:

>>> insert_at_end('abc', 'x') 
'abcx' 
>>> insert_at_end([1, 2, 4, 6], 5) 
[1, 2, 4, 6, 5] 
>>> insert_at_end((1, 2, 4, 6), 5) 
(1, 2, 4, 6, 5) 

Kể từ insert_at_end có thể xử lý iterable và không, hoạt động tốt ngay cả với:

>>> insert_at_end('abc', 'xyz') 
'abcxyz' 
>>> insert_at_end([1, 2, 4, 6], [5, 7]) 
[1, 2, 4, 6, 5, 7] 
>>> insert_at_end((1, 2, 4, 6), (5, 7)) 
(1, 2, 4, 6, 5, 7) 
+0

Điều này hoàn toàn sai. insert_at_end ([1, 2, 4, 6], [5]) phải là [1, 2, 4, 6, [5]] bởi tất cả logic. –

+0

@RomanSusi: Nếu bạn muốn xử lý chuỗi và danh sách theo cùng một cách, tôi sẽ nói rằng đó không phải là những gì tôi mong đợi. Mặt khác, thông thường tôi sẽ đồng ý với bạn; Vì vậy, tôi là người đầu tiên có chút bối rối và tôi sẽ không tranh cãi quá nhiều vì có vẻ như tác giả của bài tập cũng có logic của riêng mình ... –

Các vấn đề liên quan