如何在LOGO中进行子串替换?
How to perform sub-string substitutions in LOGO?
我想从用户那里获取字符串,解析它,然后 运行 解析的命令。
输入的字符串类似于"F20N20E10L10",保证没有空格。
我想将此输入转换为带有如下替换的 LOGO 命令:
- "F"→
fd
- "N"→
seth 0 fd
- "E"→
seth 90 fd
- "L"→
lt 90 fd
所以上面输入的字符串将转换为这些 LOGO 命令:
fd 20 seth 0 fd 20 seth 90 fd 10 lt 90 fd 10
所有 Forth 方言都允许输入和解释一串命令。
但我无法通过搜索和替换字符串操作找到任何内容。这在 LOGO 的任何方言中都可能吗?愿意考虑。
感谢您的阅读。
我已经有一段时间没有写徽标了,所以我不确定这是否是最简单的方法,但这是您可以做到的一种方法。一般的想法是您可以使用字符串作为字符列表,使用 FIRST
、LAST
、BUTFIRST
和 BUTLAST
来获取字符串的不同部分。 (我在我能找到的前两个在线 Logo 解释器上对此进行了测试 -- http://www.logointerpreter.com/turtle-editor.php and http://www.calormen.com/jslogo/ -- 两者都 运行 都很好,但你可能需要对其他 Logo 方言进行一些小的更改。)
TO RUN_COMMANDS :commands
IF (EMPTY? :commands) [STOP]
MAKE "first_command (FIRST :commands)
MAKE "rest_of_commands (BUTFIRST :commands)
IF (NOT EMPTY? :rest_of_commands) [MAKE "split (GET_NUMBER :rest_of_commands ")]
MAKE "numeric_argument (LAST :split)
MAKE "rest_of_commands (FIRST :split)
RUN_SINGLE_COMMAND :first_command :numeric_argument
RUN_COMMANDS :rest_of_commands
END
TO MERGE_STRING :word :characters
IF (NOT EMPTY? :characters) [OP (MERGE_STRING (WORD :word (FIRST :characters)) (BUTFIRST :characters))]
OP :WORD
END
TO GET_NUMBER :word :number
IF (AND (NOT (EMPTY? :word)) (IS_DIGIT (FIRST :word))) [OP (SE (GET_NUMBER (BUTFIRST :word) (LPUT (FIRST :word) :number))]
OP (SE (MERGE_STRING " :word) (MERGE_STRING " :number))
END
TO IS_DIGIT :character
OP (OR
:character = "0
:character = "1
:character = "2
:character = "3
:character = "4
:character = "5
:character = "6
:character = "7
:character = "8
:character = "9)
END
TO RUN_SINGLE_COMMAND :command :parameter
(PRINT_COMMAND :command :parameter)
IF (:command = "F) [FD :parameter]
IF (:command = "B) [BK :parameter]
IF (:command = "L) [LT 90 FD :parameter]
IF (:command = "R) [RT 90 FD :parameter]
IF (:command = "N) [SETH 0 FD :parameter]
IF (:command = "S) [SETH 180 FD :parameter]
IF (:command = "E) [SETH 90 FD :parameter]
IF (:command = "W) [SETH 270 FD :parameter]
END
TO PRINT_COMMAND :command :parameter
IF (:command = "F) [PRINT (SE "FD :parameter)]
IF (:command = "B) [PRINT (SE "BK :parameter)]
IF (:command = "L) [PRINT (SE "LT 90 "FD :parameter)]
IF (:command = "R) [PRINT (SE "RT 90 "FD :parameter)]
IF (:command = "N) [PRINT (SE "SETH 0 "FD :parameter)]
IF (:command = "S) [PRINT (SE "SETH 180 "FD :parameter)]
IF (:command = "E) [PRINT (SE "SETH 90 "FD :parameter)]
IF (:command = "W) [PRINT (SE "SETH 270 "FD :parameter)]
END
然后,试试运行宁:
RUN_COMMANDS "F20N20E10L10
这将打印并执行以下内容:
FD 20
SETH 0 FD 20
SETH 90 FD 10
LT 90 FD 10
一些解释
RUN_COMMANDS
是主要功能。它:
- 从字符串中提取第一个字母(我假设每个命令都缩写为一个字母)
- 调用
GET_NUMBER
从字符串的开头提取一个数字(可以是多个字符)。
- 将单字母缩写命令和数字传递给
RUN_SINGLE_COMMAND
- 递归重复这个过程
IS_DIGIT
在 GET_NUMBER
中用于检查一个字符是否为数字(尽管我敢打赌某些 Logo 方言具有为此的内置函数。)
使用 MERGE_STRING
是因为我有一些多字符词("word" 是一个字符串的标志),我已经把它们变成了单字符词列表,我想将列表合并回单个 Word。不过,这实际上可能不是必需的。
RUN_SINGLE_COMMAND
执行从输入字符串中解析出的每个单独的命令。我只是使用了一个大的 IF
语句,而不是像您建议的那样使用将字符串解释为代码的函数。 (一些 Logo 方言可能有这样的功能,但我不确定标准的功能。)RUN_SINGLE_COMMAND
也调用 PRINT_COMMAND
,它打印出未缩写的命令,因为它是 运行。
潜在的堆栈溢出
我使用了很多递归,因为它更符合 Logo 的习惯用法,而且 Logo 方言通常没有很多循环结构(REPEAT
除外)。但是我做的时候很粗心,因为我只是快速地写这篇文章来给你一个大概的想法。特别是,我不担心堆栈溢出(没有双关语意),我认为如果您提供长输入字符串可能会发生这种情况。为了解决这个问题,你应该确保任何可以任意多次调用的递归路径都是尾调用,Logo 会优化掉它。
乍一看,RUN_COMMANDS
没问题,但 MERGE_STRING
和 GET_NUMBER
是相反的——而不是 IF <condition> [<recursive_call>]
后跟 OUTPUT <return_value>
,最好先做 IF (NOT <condition>) [OUTPUT <return_value>]
然后再做 <recursive_call>
。测试堆栈溢出并应用此修复程序作为 reader 的练习。 :)
我想从用户那里获取字符串,解析它,然后 运行 解析的命令。
输入的字符串类似于"F20N20E10L10",保证没有空格。
我想将此输入转换为带有如下替换的 LOGO 命令:
- "F"→
fd
- "N"→
seth 0 fd
- "E"→
seth 90 fd
- "L"→
lt 90 fd
所以上面输入的字符串将转换为这些 LOGO 命令:
fd 20 seth 0 fd 20 seth 90 fd 10 lt 90 fd 10
所有 Forth 方言都允许输入和解释一串命令。
但我无法通过搜索和替换字符串操作找到任何内容。这在 LOGO 的任何方言中都可能吗?愿意考虑。
感谢您的阅读。
我已经有一段时间没有写徽标了,所以我不确定这是否是最简单的方法,但这是您可以做到的一种方法。一般的想法是您可以使用字符串作为字符列表,使用 FIRST
、LAST
、BUTFIRST
和 BUTLAST
来获取字符串的不同部分。 (我在我能找到的前两个在线 Logo 解释器上对此进行了测试 -- http://www.logointerpreter.com/turtle-editor.php and http://www.calormen.com/jslogo/ -- 两者都 运行 都很好,但你可能需要对其他 Logo 方言进行一些小的更改。)
TO RUN_COMMANDS :commands
IF (EMPTY? :commands) [STOP]
MAKE "first_command (FIRST :commands)
MAKE "rest_of_commands (BUTFIRST :commands)
IF (NOT EMPTY? :rest_of_commands) [MAKE "split (GET_NUMBER :rest_of_commands ")]
MAKE "numeric_argument (LAST :split)
MAKE "rest_of_commands (FIRST :split)
RUN_SINGLE_COMMAND :first_command :numeric_argument
RUN_COMMANDS :rest_of_commands
END
TO MERGE_STRING :word :characters
IF (NOT EMPTY? :characters) [OP (MERGE_STRING (WORD :word (FIRST :characters)) (BUTFIRST :characters))]
OP :WORD
END
TO GET_NUMBER :word :number
IF (AND (NOT (EMPTY? :word)) (IS_DIGIT (FIRST :word))) [OP (SE (GET_NUMBER (BUTFIRST :word) (LPUT (FIRST :word) :number))]
OP (SE (MERGE_STRING " :word) (MERGE_STRING " :number))
END
TO IS_DIGIT :character
OP (OR
:character = "0
:character = "1
:character = "2
:character = "3
:character = "4
:character = "5
:character = "6
:character = "7
:character = "8
:character = "9)
END
TO RUN_SINGLE_COMMAND :command :parameter
(PRINT_COMMAND :command :parameter)
IF (:command = "F) [FD :parameter]
IF (:command = "B) [BK :parameter]
IF (:command = "L) [LT 90 FD :parameter]
IF (:command = "R) [RT 90 FD :parameter]
IF (:command = "N) [SETH 0 FD :parameter]
IF (:command = "S) [SETH 180 FD :parameter]
IF (:command = "E) [SETH 90 FD :parameter]
IF (:command = "W) [SETH 270 FD :parameter]
END
TO PRINT_COMMAND :command :parameter
IF (:command = "F) [PRINT (SE "FD :parameter)]
IF (:command = "B) [PRINT (SE "BK :parameter)]
IF (:command = "L) [PRINT (SE "LT 90 "FD :parameter)]
IF (:command = "R) [PRINT (SE "RT 90 "FD :parameter)]
IF (:command = "N) [PRINT (SE "SETH 0 "FD :parameter)]
IF (:command = "S) [PRINT (SE "SETH 180 "FD :parameter)]
IF (:command = "E) [PRINT (SE "SETH 90 "FD :parameter)]
IF (:command = "W) [PRINT (SE "SETH 270 "FD :parameter)]
END
然后,试试运行宁:
RUN_COMMANDS "F20N20E10L10
这将打印并执行以下内容:
FD 20
SETH 0 FD 20
SETH 90 FD 10
LT 90 FD 10
一些解释
RUN_COMMANDS
是主要功能。它:
- 从字符串中提取第一个字母(我假设每个命令都缩写为一个字母)
- 调用
GET_NUMBER
从字符串的开头提取一个数字(可以是多个字符)。 - 将单字母缩写命令和数字传递给
RUN_SINGLE_COMMAND
- 递归重复这个过程
IS_DIGIT
在 GET_NUMBER
中用于检查一个字符是否为数字(尽管我敢打赌某些 Logo 方言具有为此的内置函数。)
MERGE_STRING
是因为我有一些多字符词("word" 是一个字符串的标志),我已经把它们变成了单字符词列表,我想将列表合并回单个 Word。不过,这实际上可能不是必需的。
RUN_SINGLE_COMMAND
执行从输入字符串中解析出的每个单独的命令。我只是使用了一个大的 IF
语句,而不是像您建议的那样使用将字符串解释为代码的函数。 (一些 Logo 方言可能有这样的功能,但我不确定标准的功能。)RUN_SINGLE_COMMAND
也调用 PRINT_COMMAND
,它打印出未缩写的命令,因为它是 运行。
潜在的堆栈溢出
我使用了很多递归,因为它更符合 Logo 的习惯用法,而且 Logo 方言通常没有很多循环结构(REPEAT
除外)。但是我做的时候很粗心,因为我只是快速地写这篇文章来给你一个大概的想法。特别是,我不担心堆栈溢出(没有双关语意),我认为如果您提供长输入字符串可能会发生这种情况。为了解决这个问题,你应该确保任何可以任意多次调用的递归路径都是尾调用,Logo 会优化掉它。
乍一看,RUN_COMMANDS
没问题,但 MERGE_STRING
和 GET_NUMBER
是相反的——而不是 IF <condition> [<recursive_call>]
后跟 OUTPUT <return_value>
,最好先做 IF (NOT <condition>) [OUTPUT <return_value>]
然后再做 <recursive_call>
。测试堆栈溢出并应用此修复程序作为 reader 的练习。 :)