虽然通常总是为工作选择正确的语言是件好事,但有时尝试用一种非常不合适的语言做某事可能是有益的。
- 它可以帮助您更好地理解问题。也许您不必像您认为的那样解决它。
- 它可以帮助您更好地理解语言。也许它支持的功能比您意识到的要多。
并将这个想法推向不合逻辑的结论......你将如何在批处理文件中实现快速排序?甚至可能吗?
虽然通常总是为工作选择正确的语言是件好事,但有时尝试用一种非常不合适的语言做某事可能是有益的。
并将这个想法推向不合逻辑的结论......你将如何在批处理文件中实现快速排序?甚至可能吗?
事实证明,这并不像你想象的那么难。语法丑得要命,但批处理语法实际上能够做一些令人惊讶的事情,包括递归、局部变量和一些令人惊讶的复杂的字符串解析。不要误会我的意思,这是一种糟糕的语言,但令我惊讶的是,它并没有完全瘫痪。我认为我没有学到任何关于快速排序的知识,但我学到了很多关于批处理文件的知识!
无论如何,这里是批处理文件中的快速排序 - 我希望您在阅读它时像我在编写它时一样尝试理解奇怪的语法。:-)
@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 有效,但会使语法比正常情况更难看。请注意,我们现在有一个混合变量,仅通过它们的名称来引用,通过在它们前面加上一个 %,通过在它们前面加上两个 %,通过将它们包装在 % 中,或者通过将它们包装在 ! 中。而且这些引用变量的不同方式几乎是完全不可互换的!
除此之外,应该比较容易理解!
这是我不久前写的一个更清晰的版本:
@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