2017-09-12 22 views
14

tôi có một đối tượng obj và một số chức năngĐang tính toán "đóng cửa" các thuộc tính của một đối tượng nhất định chức năng thay đổi các thuộc tính

def func1(obj): 
    #... 

    def func2(obj): 
    #... 

    def func3(obj): 
    #... 

rằng mỗi thay đổi các giá trị của các thuộc tính của obj.

Tôi muốn đầu vào của tôi là một cái gì đó giống như

obj = MyObject() 
obj.attr=22 

này phải được chuyển vào một chức năng closure() rằng tính tất cả applictions có thể có của các chức năng trên, có nghĩa là func1(func2(obj)), func3(func1(func1(obj))), vv lên đến một điều kiện dừng nhất định (ví dụ: không quá 20 tác phẩm chức năng).

Đầu ra phải là danh sách tất cả các kết quả đầu ra có thể cùng với tất cả các đường dẫn dẫn đến đó. Vì vậy, nếu nói 10493 là kết quả cuối cùng có thể có nếu obj.attr=22 và có hai cách để đến số 104 và một để đến tại 93. Sau đó

print closure(obj) 

nên một cái gì đó giống như

[22, 64, 21, 104] #first path to 104 through , func1(obj),func1(func1(obj)), func1(func1(func3(obj))) 

[22, 73, 104] #second path to 104 through , func3(obj),func3(func2(obj)), 

[22, 11, 93] #the only path to arrive at 94 

Làm thế nào tôi có thể thực hiện điều này? Như đã được đề xuất trong các ý kiến, điều này được thực hiện tốt nhất với cây, nhưng mặc dù tôi đã thử 2 ngày tôi hầu như không thực hiện bất kỳ tiến bộ nào thực hiện điều đó (tôi mới dùng Python/lập trình)!
Ví dụ của tôi đơn giản đến mức thay vì func(obj) chúng tôi có thể trực tiếp sử dụng func(22) nhưng ví dụ tôi cần làm là phức tạp hơn, nơi tôi chắc chắn sẽ cần sử dụng đối tượng, vì vậy đây sẽ là một ví dụ làm việc tối thiểu cho điều đó.

Cây có thể không phải là cây đầy đủ, vì mỗi ứng dụng chức năng sẽ chứa thử nghiệm cho dù nó có thể được áp dụng cho trạng thái hiện tại (thuộc tính) của obj và trong một số trường hợp, thử nghiệm sẽ không thành công (các thuộc tính) obj không đổi.

+0

Có vẻ như bạn muốn có cấu trúc cây nơi mỗi nút là một trạng thái và phân nhánh xảy ra bởi các hàm (mỗi hàm nhận trạng thái và tạo một nút/trạng thái ** mới **). Đây là thực hành phổ biến trong AI và các lĩnh vực khác và có thể hữu ích ở đây. Để theo dõi những gì đã xảy ra, chỉ cần quay lại thư mục gốc khi bạn đã đạt đến trạng thái bắt buộc. Oh, và làm cho nó BFS nếu có thể :) –

+0

@ReutSharabani Có, đó có vẻ như những gì tôi muốn tôi nghĩ, thx. Nhưng tại sao lại sử dụng BFS? – user47574

+1

Vâng, nếu bạn sử dụng DFS và bạn có một đường dẫn ứng dụng vô hạn của các chức năng ... Bạn sẽ không bao giờ tìm thấy bất cứ điều gì có ý nghĩa ngay cả khi có một "giải pháp". Tuy nhiên, nếu có một giải pháp, DFS đảm bảo tìm nó (nhưng bạn trả tiền trong bộ nhớ, không giống DFS là bộ nhớ không đổi). Bạn cũng có thể sử dụng DFS lặp lại (google it) để đảm bảo nó dừng lại. –

Trả lời

3

Dưới đây là một ví dụ đơn giản cố gắng tìm một số (goal) là tiền thân của số khác (inital_state) khi áp dụng quy tắc trong Collatz conjecture.

Trong ví dụ của bạn, objstate[func1, func2, ...]functions trong ví dụ của tôi. Phiên bản này sẽ trả về một đường dẫn đến đầu ra cuối cùng, giảm thiểu số lượng các ứng dụng chức năng. Thay vì tìm kiếm, bạn có thể liệt kê tất cả các tiểu bang bằng cách xóa bài kiểm tra mục tiêu và trả lại prev_states sau khi kết thúc vòng lặp.

from collections import deque 

def multiply_by_two(x): 
    return x * 2 

def sub_one_div_three(x): 
    if (x - 1) % 3 == 0: 
     return (x - 1) // 3 
    else: 
     return None # invalid 


functions = [multiply_by_two, sub_one_div_three] 

# find the path to a given function 
def bfs(initial_state, goal): 
    initial_path = [] 
    states = deque([(initial_state, initial_path)])  # deque of 2-tuples: (state, list of functions to get there) 
    prev_states = {initial_state}      # keep track of previously visited states to avoid infinite loop 

    while states: 
     # print(list(map(lambda x: x[0], states)))  # print the states, not the paths. useful to see what's going on 
     state, path = states.popleft() 

     for func in functions: 
      new_state = func(state) 

      if new_state == goal:      # goal test: if we found the state, we're done 
       return new_state, path + [func] 

      if (new_state is not None and   # check that state is valid 
       new_state not in prev_states):  # and that state hasn't been visited already 
       states.append((new_state, path + [func])) 
      prev_states.add(new_state)    # make sure state won't be added again 
    else: 
     raise Exception("Could not get to state") 

print(functions) 
print(bfs(1, 5)) 

# prints (5, [<function multiply_by_two at 0x000002E746727F28>, <function multiply_by_two at 0x000002E746727F28>, <function multiply_by_two at 0x000002E746727F28>, <function multiply_by_two at 0x000002E746727F28>, <function sub_one_div_three at 0x000002E7493C9400>]). You can extract the path from here. 
+0

Tôi hiện đang cố gắng hiểu mã (xin hãy theo tôi, vì tôi là người mới bắt đầu), để tôi có thể thích ứng với nhu cầu của riêng mình. Dường như chức năng bfs của bạn không chỉ tính toán các bfs của một đồ thị nhất định, mà còn trực tiếp tạo ra đồ thị, đúng không? Đây có phải là lựa chọn thiết kế tiêu chuẩn hay khả năng tạo ra một lớp/đối tượng cây trong đó mỗi nút là danh sách của biểu mẫu 'func3 (func1 (func (2 (...)))' và sau đó sử dụng một bfs để đi qua nó? Ít nhất là khái niệm đó là cách tôi đã làm nó. – user47574

+0

Bạn nói đúng, tôi không tính toán đồ thị/cây một cách rõ ràng. Nếu bạn cần thực hiện nhiều thao tác hơn trên biểu đồ, nó sẽ có ý nghĩa để cache nó, nhưng nếu đây là thao tác duy nhất bạn cần, không có lý do gì để xây dựng nó một cách rõ ràng – user3080953

+0

Tôi thấy chỉnh sửa của bạn về việc sử dụng các đối tượng thay vì int. Để khắc phục điều đó, bạn cần phải làm cho trạng thái băm (thực hiện '__hash__') và làm cho' func (state) 'trả về một đối tượng, không sửa đổi' trạng thái' – user3080953

1

Âm thanh vui nhộn, hãy chia nhỏ các bước.

  1. Tìm ra các kết hợp chức năng.

  2. Đánh giá các kết hợp có thể có của hàm.

Tìm ra tổ hợp có thể

Một cách để làm điều này là sử dụng máy phát điện. Chúng hiệu quả về mặt bộ nhớ, vì vậy bạn không phải tạo ra một loạt các giá trị và tối đa hóa vùng heap.

Vậy làm thế nào để chúng tôi có được tất cả các kết hợp. Tìm kiếm nhanh các tài liệu Python đề xuất sử dụng các công cụ lặp. Vì vậy, hãy làm điều đó.

from itertools import combinations 

def comb(fns, n): 
    return combinations(fns, n) 

Cho đến nay chúng tôi có máy phát có thể cung cấp cho chúng tôi tất cả các kết hợp (không thay thế) danh sách chức năng. Mỗi kết hợp được cung cấp sẽ là danh sách các hàm số n lớn. Chúng ta có thể đơn giản gọi từng lần và chúng ta có thể nhận được kết quả sáng tác.

Đó là một cấp trong cây. Làm thế nào để chúng ta có được cấp độ tiếp theo. Vâng, chúng ta có thể nhận được tất cả các kết hợp của kích thước 1 và sau đó tất cả các kết hợp của kích thước 2 và như vậy. Kể từ khi máy phát điện là lười biếng, chúng tôi aught để có thể làm điều này mà không thổi lên người phiên dịch.

def combo_tot(fns): 
    start=1 
    while (start <= len(fns)): 
     for c in comb(fns, start): 
      yield c 
     start += 1 

Đánh giá kết hợp có thể

Vì vậy, bây giờ chúng tôi có tất cả các kết hợp có thể có ý nghĩa. Hãy sử dụng điều này để đánh giá nội dung.

def evalf(fns_to_compose, initval): 
    v = initval 
    for fn in fns_to_compose: 
     v = fn(v) 
    return v 

Vậy đó là phần thứ hai. Bây giờ tất cả những gì bạn cần làm là chuỗi em.

def results(fns, init): 
    return (evalf(fn, init) for fn in combo_tot(fns)) 

Bây giờ, chỉ cần lấy bao nhiêu kết quả tùy theo nhu cầu của bạn.

Nhược điểm

Tương tự như với bất kỳ phương pháp mà không clone obj. Nó sẽ biến đổi đối tượng. Hơn nữa, chúng tôi có chi phí của máy phát điện (có thể hơi chậm hơn so với các vòng lặp). Chúng tôi cũng có một hit để dễ đọc (đặc biệt là nếu một người nào đó không quen thuộc với máy phát điện).

Tuyên bố từ chối trách nhiệm: Tôi đang nhập địa chỉ này trên điện thoại. Lỗi chính tả nhỏ có thể tồn tại.

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