Efficient recursion in functional programming vs. inefficient recursion in different paradigms
-
23-09-2019 - |
문제
As far as I know recursion is very elegant but unefficient in OOP and procedural programming (see the wonderful "High Order perl", Mark Jason Dominus). I had some informations that in functional programming recursion is fast - keeping its elegance and simplicity. Could someone confirm and possibly amplify this? I am thinking in terms of XSLT and Haskell (high on my next-language-to-learn list)
Thanks
Daniel
해결책
Tail recursion is iteration in any decent functional language implementation. Here's an example using GHC Haskell. A simple program to add a sequence of numbers. It begins as the composition of several recursive functions:
import qualified Data.Vector as U
main = print (U.sum (U.enumFromTo 1 (10000000 :: Int)))
Which the compiler optimizes into a single tail recursive function (in a source-to-source transformation):
loop x y = case y <= y 10000000 of
False -> x
True -> loop (x + y) (y + 1)
This recursive function is then compiled into a straight forward loop:
loop:
.Lc216:
cmpq $10000000,%rsi
jle .Lc219
movq %r14,%rbx
movq (%rbp),%rax
jmp *(%rax)
.Lc219:
addq %rsi,%r14
incq %rsi
jmp loop
Or with the GHC LLVM backend, additional optimizations are applied to the imperative representation of the program:
loop:
leaq 1(%rsi), %rax
addq %rsi, %r14
cmpq $10000001, %rax
jge .LBB1_5
addq $2, %rsi
addq %rax, %r14
test: # %tailrecurse
cmpq $10000001, %rsi
jl loop
Note how the tail recursive label is tagged.
So we had a pipeline of recursive functions, which were compiled to a single tail recursive function, which was compiled to a single imperative loop using no stack. And 8 instructions in the end.
And that is why both function composition, and recursion, are extremely efficient in good, optimizing function languages.
다른 팁
OOP/Procedural languages tend to place data on the stack each time a recursive call is made - thus recursion is not as efficient as iteration in these languages.
By contrast, compilers/interpreters for functional languages are typically designed to optimize tail recursion to be as efficient as iteration:
Recursion may require maintaining a stack, but tail recursion can be recognized and optimized by a compiler into the same code used to implement iteration in imperative languages. The Scheme programming language standard requires implementations to recognize and optimize tail recursion. Tail recursion optimization can be implemented by transforming the program into continuation passing style during compilation, among other approaches.
what-is-tail-call-optimization and which-languages-support-tail-recursion-optimization have more detailed information.
If the compiler in use supports the tail call optimization and you structure your code to take advantage of it, recursion isn't inefficient.
Due to the prevelance of recursion in functional programming, compilers for functional languages are more likely to implement the tail call optimization that procedural ones.
Efficient recursion in XSLT
There are two main ways to achieve efficient recursion in XSLT:
- Tail-recursion optimization
- Divide and Conquer (DVC)
There are a lot of answers covering tail recursion, so here's just a simple example:
<xsl:function name="my:sum">
<xsl:param name="pAccum" as="xs:double*"/>
<xsl:param name="pNums" as="xs:double*"/>
<xsl:sequence select=
"if(empty($pNums))
then $pAccum
else
my:sum($pAccum + $pNums[1], $pNums[position() >1])
"
/>
</xsl:function>
One can check that my:sum(0, 1 to 100) is evaluated to: 5050.
Here is how one would implement the sum()
function in a DVC way:
<xsl:function name="my:sum2">
<xsl:param name="pNums" as="xs:double*"/>
<xsl:sequence select=
"if(empty($pNums))
then 0
else
if(count($pNums) eq 1)
then $pNums[1]
else
for $half in count($pNums) idiv 2
return
my:sum2($pNums[not(position() gt $half)])
+
my:sum2($pNums[position() gt $half])
"
/>
</xsl:function>
The main idea behind DVC is to subdivide the input sequence into two (usually) or more parts and to process them independently from one another, then to combine the results in order to produce the result for the total input sequence.
Note that for a sequence of N
items, the maximum depth of the call stack at any point od time would not exceed log2(N)
, which is more than enough for most practical purposes. For example, the maximum depth of the call stack when processing a sequence of 1000000 (1M) items, would be only 19.
While there are some XSLT processors that are not smart enough to recognize and optimize tail-recursion, a DVC-recursive template works on any XSLT processor.
나는 다음을 사용하고 태그의 수를 얻었습니다
var tags = xdoc.Descendants(z + "row")
.Select(r => (string)r.Attribute("ows_Article_x0020_Tags"));
// Join all the tags to form a string
string result = String.Join("", tags);
//Remove id's of the tags from the string
result = Regex.Replace(result, @"\d", "");
//remove special characters from the string to get a list of tags
var tagTokens = result.Split(new string[]{";#"},StringSplitOptions.RemoveEmptyEntries).ToList();
//Get the count of each tag being used in the list
var tokenGroups = tagTokens.GroupBy(token => token).ToDictionary(token=> token.Key, tokenval=>tokenval.Count());
. If the language isn't optimized by the compiler, recursion is very likely to be slower than iteration, because on top of descending down given lane, which is pretty much equivalent to iteration, you have to backtrace your steps back to top upon finishing the job.
Otherwise, it is pretty much equivalent, except it may be much more elegant, as you let the compiler and system handle the loop behind the scenes. And of course there are tasks (like processing tree-like structures) where recursion is the only way (or at least the only that isn't hopelessly convoluted).
다음 오류가 나타납니다.
라인 2에서는 $customcss
에 자원을 넣습니다 ( 설명서 ).그런 다음 비위임 인 문자열 (3 행)을 추가하려고했습니다.따라서 2 번에서 var를 변경하십시오.
코드를 통해 할 수 있습니다 :
예 :
SPList list = web.Lists["My List"];
list.EnableVersioning = true;
.
목록을 만드는 경우 위의 코드에 간단히 추가 할 수 있습니다.
somthing lines :
//get web object from context
SPWeb web = SPContext.Current.Web;
//get list collection
SPListCollection lists = Web.Lists;
//add list to list collection
lists.Add("My List", "My list for user input form", SPListTemplateType.GenericList);
//call list that we just made
SPList newMyList = Web.Lists["My List"];
//enable versioning on that list
newMyList.EnableVersioning = true;
.
http://msdn.microsoft.com/en-us/library. /microsoft.sharepoint.splist.enableVersioning.aspx
편집
나는 당신을 얻습니다! VS2012를 사용하여 목록을 추가 할 때 목록의 XML (element.xml) :
.
<ListInstance
CustomSchema = string
Description = "Text"
DocumentTemplate = integer
FeatureId = "Text"
Hidden = "TRUE" | "FALSE"
HyperlinkBaseUrl = string
Id = Text
OnQuickLaunch = "TRUE" | "FALSE"
QuickLaunchUrl = "Text"
RootWebOnly = "TRUE" | "FALSE"
TemplateType = Integer
Title = "Text"
Url = "Text"
VersioningEnabled = "TRUE" | "FALSE"
</ListInstance>
목록 elements.xml 목록에 추가됩니다. ListInstance에서 VersionInstance를 볼 수 있으므로 다음과 같이 추가합니다. versioningEnabled= true 및 버전 관리가 해당 사용자 정의 목록에 사용됩니다.
주요 개념과 동일한 개념 인 주요 버전의 경우 :
http://msdn.microsoft.com/en-us/library/office/ms415091. .aspx
.
<List
BaseType = "Integer" | "Text"
Default = "TRUE" | "FALSE"
DefaultItemOpen = "Integer"
Description = "Text"
Direction = "Text"
DisableAttachments = "TRUE" | "FALSE"
DraftVersionVisibility = "Integer"
EnableContentTypes = "TRUE" | "FALSE" EnableMinorVersions = "TRUE" | "FALSE" EnableThumbnails = "TRUE" | "FALSE"
EventSinkAssembly = "Text"
EventSinkClass = "Text"
EventSinkData = "Text"
FolderCreation = "TRUE" | "FALSE"
Id = "GUID"
ModeratedList = "TRUE" | "FALSE"
ModerationType = "TRUE" | "FALSE"
Name = "Text"
OrderedList = "TRUE" | "FALSE"
PrivateList = "TRUE" | "FALSE"
QuickLaunchUrl = "URL"
RootWebOnly = "TRUE" | "FALSE"
ThumbnailSize = "Integer"
Title = "Text"
Type = "Integer"
Url = "URL"
URLEncode = "TRUE" | "FALSE"
VersioningEnabled = "TRUE" | "FALSE"
WebImageHeight = "Integer"
WebImageWidth = "Integer">
</List>
elements.xml 목록에 속한 schema.xml에 위의 사항을 추가합니다.
http://msdn.microsoft.com/en-us/library/office/ms459356. .aspx