11

虽然通常总是为工作选择正确的语言是件好事,但有时尝试用一种非常不合适的语言做某事可能是有益的。

  1. 它可以帮助您更好地理解问题。也许您不必像您认为的那样解决它
  2. 它可以帮助您更好地理解语言。也许它支持的功能比您意识到的要多。

并将这个想法推向不合逻辑的结论......你将如何在批处理文件中实现快速排序?甚至可能吗?

4

2 回答 2

23

事实证明,这并不像你想象的那么难。语法丑得要命,但批处理语法实际上能够做一些令人惊讶的事情,包括递归、局部变量和一些令人惊讶的复杂的字符串解析。不要误会我的意思,这是一种糟糕的语言,但令我惊讶的是,它并没有完全瘫痪。我认为我没有学到任何关于快速排序的知识,但我学到了很多关于批处理文件的知识!

无论如何,这里是批处理文件中的快速排序 - 我希望您在阅读它时像我在编写它时一样尝试理解奇怪的语法。:-)

@echo off
SETLOCAL ENABLEDELAYEDEXPANSION

call :qSort %*
for %%i in (%return%) do set results=!results! %%i
echo Sorted result: %results%
ENDLOCAL
goto :eof

:qSort
SETLOCAL
    set list=%*
    set size=0
    set less=
    set greater=
    for %%i in (%*) do set /a size=size+1
    if %size% LEQ 1 ENDLOCAL & set return=%list% & goto :eof
    for /f "tokens=2* delims== " %%i in ('set list') do set p=%%i & set body=%%j
    for %%x in (%body%) do (if %%x LEQ %p% (set less=%%x !less!) else (set greater=%%x !greater!))
    call :qSort %less%
    set sorted=%return%
    call :qSort %greater%
    set sorted=%sorted% %p% %return%
ENDLOCAL & set return=%sorted%
goto :eof

通过给它一组数字来调用它,以便在命令行上排序,用空格分隔。例子:

C:\dev\sorting>qsort.bat 1 3 5 1 12 3 47 3
Sorted result:  1 1 3 3 3 5 12 47

代码有点难理解。它基本上是标准的快速排序。关键是我们将数字存储在一个字符串中——穷人的数组。第二个 for 循环非常模糊,它基本上将数组拆分为头部(第一个元素)和尾部(所有其他元素)。Haskell 使用符号 x:xs 执行此操作,但批处理文件使用 /f 开关调用的 for 循环执行此操作。为什么?为什么不?

SETLOCAL 和 ENDLOCAL 调用让我们可以做局部变量 - 有点像。SETLOCAL 为我们提供了原始变量的完整副本,但是当我们调用 ENDLOCAL 时,所有更改都被完全擦除,这意味着您甚至无法使用全局变量与调用函数进行通信。这解释了丑陋的“ENDLOCAL & set return=%sorted%”语法,尽管逻辑表明它实际上是有效的。执行该行时,由于该行尚未执行,因此尚未擦除已排序的变量-然后由于该行已执行,因此未擦除返回变量。合乎逻辑!

此外,有趣的是,您基本上不能在 for 循环中使用变量,因为它们无法更改 - 这消除了 for 循环的大部分意义。解决方法是设置 ENABLEDELAYEDEXPANSION 有效,但会使语法比正常情况更难看。请注意,我们现在有一个混合变量,仅通过它们的名称来引用,通过在它们前面加上一个 %,通过在它们前面加上两个 %,通过将它们包装在 % 中,或者通过将它们包装在 ! 中。而且这些引用变量的不同方式几乎是完全不可互换的!

除此之外,应该比较容易理解!

于 2008-09-25T12:55:44.563 回答
5

这是我不久前写的一个更清晰的版本:

@echo off

echo Sorting:  %*

set sorted=

:sort
:: If we've only got one left, we're done.
if "%2"=="" (
  set sorted=%sorted% %1
  :: We have to do this so that sorted gets actually set before we print it.
  goto :finalset
)
:: Check if it's in order.
if %1 LEQ %2 (
  :: Add the first value to sorted.
  set sorted=%sorted% %1
  shift /1
  goto :sort
)
:: Out of order.
:: Reverse them and recursively resort.
set redo=%sorted% %2 %1
set sorted=
shift /1
shift /1
:loop
if "%1"=="" goto :endloop
set redo=%redo% %1
shift /1
goto :loop
:endloop
call :sort %redo%
:: When we get here, we'll have already echod our result.
goto :eof

:finalset
echo Final Sort:  %sorted%
goto :eof

例子:

C:\Path> sort 19 zebra blah 1 interesting 21 bleh 14 think 2 ninety figure it out

产生:

Sorting:  19 zebra blah 1 interesting 21 bleh 14 think 2 ninety figure it out
Final Sort:   1 2 14 19 21 blah bleh figure interesting it ninety out think zebra
于 2011-02-11T03:45:12.283 回答