python->scheme转换的问题

发布于 2024-07-11 00:32:45 字数 1282 浏览 6 评论 0原文

我目前正在尝试使用方案语义编写一个Python程序,这样我以后就可以将其转换为Scheme,而无需依赖大量Pythonic的东西。

我正在尝试使用 a*、深度优先和广度优先搜索算法来解决滑动拼图问题(其中有 9 个插槽和 8 个方块排列在一个正方形中)。 我大约 11 年前在 Lisp 的一些 AI 课程中这样做过,但基本上当时我对 lisp 毫无了解,我全心全意地讨厌它,只有回想起来,我才意识到我正在用 Lisp 编程“C”。 教授在这件事上没有提供任何帮助。

我有一个 python 函数,可以轻松交换两个图块:

def swap(p, (r1, c1), (r2, c2)):  
    # Swaps *any* two locations and returns new configuration  
    # Does not concern itself with zero location, etc  
    # Not sure how to do this functionally  
    p_p = p[:]  
    temp = p_p[r1][c1]  
    p_p[r1][c1] = p_p[r2][c2]  
    p_p[r2][c2] = temp  
    return p_p  

我想将其转换为您可能在 SICP 中找到的东西,避免副作用等。

但这提出了一个问题。 我在 SICP 中读到的所有内容都是通过递归进行循环。 我没有看到在恒定时间内访问数组/向量/列表的任何内容。 我可以想象一种循环/递归的方式来读取元素,但我发现很难想象一种方法来创建一个更改了某个元素的新列表,而不调用产生诸如 set! 之类的副作用,并且不诉诸疯狂的 if /then/else 子句涉及应更改哪个元素。 当考虑二维数组时,这当然会变得更加混乱。 在这种情况下,使用 python 的解决方案是显而易见的,因为它本身支持多维数组。

在 C/C++/Python/Matlab/Lua/其他任何语言中,通过 [i] 语法访问列表/数组很容易,并且直接转换为底层面向硬件的指针查找。 我不明白scheme是如何做到这一点的,因为scheme的SICP版本中定义了原子操作,这些操作看起来都非常面向循环和搜索。 矢量和列表数组访问函数如何获得恒定时间访问? (我是这里的新手,所以我不确定我会谈论什么功能)。 是否有 C 或汇编库正在被秘密访问? 方案中是否有任何固有的常量时间语义可用于列表/数组/向量访问,并且这将允许我暂时在 Python 中使用该习惯用法而无负罪感?

如何使用 Schemish 语义在 python 中重写上述函数? 我如何在Scheme中重写上述函数?

I currently am trying to write a Python program using scheme semantics so I can later translate it into Scheme without relying on a lot of Pythonic stuff.

I'm trying solve the sliding puzzle problem (where you have 9 slots and 8 tiles arranged in a square) using a*, depth first, and breadth first search algorithm. I did this ~11 years ago in some AI class in Lisp, but basically at the time I had no idea about lisp, I hated it with all my heart, and only in retrospect do I realize I was programming "C" in Lisp. The prof didn't help in this matter.

I have a python function which can swap two tiles easily:

def swap(p, (r1, c1), (r2, c2)):  
    # Swaps *any* two locations and returns new configuration  
    # Does not concern itself with zero location, etc  
    # Not sure how to do this functionally  
    p_p = p[:]  
    temp = p_p[r1][c1]  
    p_p[r1][c1] = p_p[r2][c2]  
    p_p[r2][c2] = temp  
    return p_p  

I'd like to turn this into something you might find in SICP, avoiding side effects, etc.

But this brings up a question. Everything I read in SICP is loops via recursion. I didn't see anything in accessing arrays/vectors/lists in constant time. I can imagine a loopish/recursive way to read an element, but I find it harder to imagine a way to create a new list with a certain element changed, without invoking side-effect producing things like set!, and without resorting to crazy if/then/else clauses concerning which element should be changed. This of course gets more confusing when considering a 2d array. In this case the solution with python is obvious because of its native support for multidimensional arrays.

In C/C++/Python/Matlab/Lua/anything else, accessing lists/arrays via the [i] syntax is easy, and directly translates to a hardware-oriented pointer lookup somewhere underneath. I don't understand how scheme does this, given the atomic operations defined in the SICP version of scheme, which all seem very loop-and-search oriented. How do the vector and list array access functions work to get constant time access? (I'm a total newbie here, so I'm not ever sure what functions I'd be talking about). Is there a C or Assembly library someplace which is secretly being accessed? Are there any inherent constant-time semantics in scheme which could be used for list/array/vector access, and which would allow me a guilt-free way of using that idiom in Python for the moment?

How would can I rewrite the above function in python using Schemish semantics? How would I rewrite the above function in Scheme?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

仙女 2024-07-18 00:32:45

您发现最初的问题是尝试用 Lisp 编写 C 语义。 尝试用python写scheme语义不是在重蹈覆辙吗? 我总是尝试将 X 语言作为一种范式来学习,就像学习一种语言一样,并以最 X 风格的方式进行写作。

如果这是一个您知道将要迁移的业务应用程序,那么这可能是合理的,但否则我只是一开始就将其编写在方案中。

You identified that your initial problem was trying to write C semantics in Lisp. Is it not repeating the mistake to try to write scheme semantics in python? I always try to learn language X as a paradigm as much as a language and write in the most x-ish way.

It might be justifiable if this was a business app you knew was going to be migrated, but otherwise I'd just write it in scheme to begin with.

回眸一笑 2024-07-18 00:32:45

大约一年前,我用 Lisp 编写了一个 8 谜题求解器。 我只使用了 3 个列表的列表,每个子列表有 3 个元素是数字。 它不是恒定时间,但它是可移植的。

无论如何,如果您真的有兴趣在功能上执行此操作(方案不要求您这样做),最简单的方法是创建一些辅助函数,这些函数将获取给定行/列的特定值并“设置”给定行/的值上校 设置操作不会修改原始数据结构,而是基于旧状态构造新状态。

然后你可以根据这些get和set操作编写一个交换操作。 这是我一年前在 Common Lisp 中写的内容,但它很容易转换为 Scheme:

; getval
;
; This function takes a position (r . c) where and returns the corresponding
; number in the 8-puzzle state. For example, if you wanted (1 . 2) from
; ((1 2 3) (4 5 6) (7 8 9)), the value would be 6. The r and c values begin
; at 0.
;
; parameters:  pos    The position to get
;              state  The 8-puzzle state
; returns:     The value at pos in state
(defun getval (pos state)
  (if (null state) 'no-value
      (if (= 0 (car pos))
      (if (= 0 (cdr pos))
          (caar state)
          (getval (cons (car pos) (- (cdr pos) 1)) (list (cdar state))))
      (getval (cons (- (car pos) 1) (cdr pos)) (cdr state)))))

; setval
;
; This function returns a state where the value at pos is replaced by val.
; Like getval, this function is zero-based. Accessing beyond the size of
; the state is undefined (and probably broken)
;
; parameters:  pos    Position to set
;              val    Value to set
;              state  State to modify
; returns:     New state where pos is val
(defun setval (pos val state)
  (if (null state) '()
      (if (= 0 (car pos))
      (if (= 0 (cdr pos))
          (cons (cons val (cdar state)) (cdr state))
          (let ((temp (setval (cons (car pos) (- (cdr pos) 1)) val
                  (cons (cdar state) (cdr state)))))
        (cons (cons (caar state) (car temp)) (cdr temp))))
      (cons (car state) (setval (cons (- (car pos) 1) (cdr pos)) val (cdr state))))))

; state-swap
;
; This function takes a state and two positions and returns a new state with
; the values in those two positions swapped.
;
; parameters:  state  State to swap within
;              a      Position to swap with b
;              b      Position to swap with a
; return:      State with a swapped with b
(defun state-swap (state a b)
  (let ((olda (getval a state)) (oldb (getval b state)))
    (setval a oldb (setval b olda state))))

I wrote an 8-puzzle solver in Lisp about a year ago. I just used a list of 3 lists, each sublist with 3 elements being the numbers. It's not constant time, but it is portable.

Anyways, if you are really interested in doing this functionally (Scheme doesn't require you to) what is easiest to do is to create some helper functions that will get a specific value given row/col and 'set' a value given row/col. Instead of modifying the original data structure, the set operation will construct the new state based on the old state.

Then you can write a swap operation based on these get and set operations. Here's what I wrote about a year ago in Common Lisp, but it's easily convertible to Scheme:

; getval
;
; This function takes a position (r . c) where and returns the corresponding
; number in the 8-puzzle state. For example, if you wanted (1 . 2) from
; ((1 2 3) (4 5 6) (7 8 9)), the value would be 6. The r and c values begin
; at 0.
;
; parameters:  pos    The position to get
;              state  The 8-puzzle state
; returns:     The value at pos in state
(defun getval (pos state)
  (if (null state) 'no-value
      (if (= 0 (car pos))
      (if (= 0 (cdr pos))
          (caar state)
          (getval (cons (car pos) (- (cdr pos) 1)) (list (cdar state))))
      (getval (cons (- (car pos) 1) (cdr pos)) (cdr state)))))

; setval
;
; This function returns a state where the value at pos is replaced by val.
; Like getval, this function is zero-based. Accessing beyond the size of
; the state is undefined (and probably broken)
;
; parameters:  pos    Position to set
;              val    Value to set
;              state  State to modify
; returns:     New state where pos is val
(defun setval (pos val state)
  (if (null state) '()
      (if (= 0 (car pos))
      (if (= 0 (cdr pos))
          (cons (cons val (cdar state)) (cdr state))
          (let ((temp (setval (cons (car pos) (- (cdr pos) 1)) val
                  (cons (cdar state) (cdr state)))))
        (cons (cons (caar state) (car temp)) (cdr temp))))
      (cons (car state) (setval (cons (- (car pos) 1) (cdr pos)) val (cdr state))))))

; state-swap
;
; This function takes a state and two positions and returns a new state with
; the values in those two positions swapped.
;
; parameters:  state  State to swap within
;              a      Position to swap with b
;              b      Position to swap with a
; return:      State with a swapped with b
(defun state-swap (state a b)
  (let ((olda (getval a state)) (oldb (getval b state)))
    (setval a oldb (setval b olda state))))
感悟人生的甜 2024-07-18 00:32:45

这是实现它的一种方法。 使用将应用适当映射的函数重新创建列表。

def swap(p, (r1,c1), (r2,c2)):
    def getitem(r,c):
        if (r,c) == (r1,c1): return p[r2][c2]
        elif (r,c) == (r2,c2): return p[r1][c1]
        return p[r][c]
    return [ [getitem(r,c) for c in range(len(p[0]))] for r in range(len(p)) ]

您甚至可以更进一步,使该函数成为实际的接口,其中每个交换仅返回一个函数,该函数在传递到下面的函数之前执行适当的转换。 性能不是特别好,但是相当简单的函数方法,无需使用令人讨厌的可变数据结构:

def swap(f, (r1,c1), (r2,c2)):
    def getitem(r,c):
        if (r,c) == (r1,c1): return f(r2,c2)
        elif (r,c) == (r2,c2): return f(r1,c1)
        return f(r,c)
   return getitem

l=[ [1,2,3], [4,5,6], [7,8,0]]
f=lambda r,c: l[r][c]    # Initial accessor function
f=swap(f, (2,1), (2,2))  # 8 right
f=swap(f, (1,1), (2,1))  # 5 down
print [[f(x,y) for y in range(3)] for x in range(3)]
# Gives: [[1, 2, 3], [4, 0, 6], [7, 5, 8]]

Here's one way to achieve it. Recreate the list using a function which will apply the appropriate mapping.

def swap(p, (r1,c1), (r2,c2)):
    def getitem(r,c):
        if (r,c) == (r1,c1): return p[r2][c2]
        elif (r,c) == (r2,c2): return p[r1][c1]
        return p[r][c]
    return [ [getitem(r,c) for c in range(len(p[0]))] for r in range(len(p)) ]

You could even take this a step further and make the function be the actual interface, where each swap merely returns a function that does the appropriate conversions before passing through to the function below. Not particularly performant, but a fairly simple functional approach that dispenses with nasty mutable datastructures:

def swap(f, (r1,c1), (r2,c2)):
    def getitem(r,c):
        if (r,c) == (r1,c1): return f(r2,c2)
        elif (r,c) == (r2,c2): return f(r1,c1)
        return f(r,c)
   return getitem

l=[ [1,2,3], [4,5,6], [7,8,0]]
f=lambda r,c: l[r][c]    # Initial accessor function
f=swap(f, (2,1), (2,2))  # 8 right
f=swap(f, (1,1), (2,1))  # 5 down
print [[f(x,y) for y in range(3)] for x in range(3)]
# Gives: [[1, 2, 3], [4, 0, 6], [7, 5, 8]]
下壹個目標 2024-07-18 00:32:45

酷,谢谢你的 lisp 代码。 我需要研究它以确保我明白。

至于第一个答案,我第一次用 lisp“写 c”,因为这是我知道如何编程的唯一方法,并且不知道为什么有人会使用 lisp。 这一次,我一直在玩scheme,但想使用python,所以如果我陷入困境,我可以“作弊”并使用pythonish,然后在等待usenet答案的同时继续解决问题的下一部分。

Cool, thanks for the lisp code. I'll need to study it to make sure I get it.

As for the first answer, the first time I was "writing c" in lisp because that's the only way I knew how to program and didn't have a clue why anyone would use lisp. This time around, I've been playing around with scheme, but wanted to use python so if I got stuck on something I could "cheat" and use something pythonish, then while waiting for usenet answers go on to the next part of the problem.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文