使用右折叠和差异列表对列表进行 Church 编码

2024-03-04

这是之后的连续问题

如何存储Monoidal List函数链的数据? https://stackoverflow.com/questions/51297054/how-to-store-data-of-a-functional-chain-of-monoidal-list

and

从没有数组的函数链中提取数据 https://stackoverflow.com/questions/51418212/extracting-data-from-a-function-chain-without-arrays

在这里,我想对我的问题的贡献者表示尊重和感谢,特别是 @Aadit M Shah 和 @user633183

现在提出这个问题是为了澄清两者之间的异同或关系差异列表 https://en.wikipedia.org/wiki/Difference_list and 教会名单 https://en.wikipedia.org/wiki/Church_encoding#Represent_the_list_using_right_fold

.


差异列表

https://stackoverflow.com/a/51320041/6440264 https://stackoverflow.com/a/51320041/6440264

A 差异清单 https://en.wikipedia.org/wiki/Difference_list是一个函数,它接受一个列表并在其前面添加另一个列表。例如:

const concat = xs => ys => xs.concat(ys); // This creates a difference list.

const f = concat([1,2,3]); // This is a difference list.

console.log(f([])); // You can get its value by applying it to the empty array.

console.log(f([4,5,6])); // You can also apply it to any other array.

差异列表最酷的一点是它们形成了一个幺半群,因为它们只是内功能 https://en.wikipedia.org/wiki/Endomorphism#Endofunctions:

const id = x => x; // The identity element is just the id function.

const compose = (f, g) => x => f(g(x)); // The binary operation is composition.

compose(id, f) = f = compose(f, id);                   // identity law
compose(compose(f, g), h) = compose(f, compose(g, h)); // associativity law

更好的是,您可以将它们打包到一个简洁的小类中,其中函数组合是点运算符:

class DList {
    constructor(f) {
        this.f  = f;
        this.id = this;
    }

    cons(x) {
        return new DList(ys => this.f([x].concat(ys)));
    }

    concat(xs) {
        return new DList(ys => this.f(xs.concat(ys)));
    }

    apply(xs) {
        return this.f(xs);
    }
}

const id = new DList(x => x);

const cons = x => new DList(ys => [x].concat(ys));   // Construct DList from value.

const concat = xs => new DList(ys => xs.concat(ys)); // Construct DList from array.

id . concat([1, 2, 3]) = concat([1, 2, 3]) = concat([1, 2, 3]) . id // identity law

concat([1, 2]) . cons(3) = cons(1) . concat([2, 3]) // associativity law

您可以使用apply方法来检索的值DList如下:

class DList {
    constructor(f) {
        this.f  = f;
        this.id = this;
    }

    cons(x) {
        return new DList(ys => this.f([x].concat(ys)));
    }

    concat(xs) {
        return new DList(ys => this.f(xs.concat(ys)));
    }

    apply(xs) {
        return this.f(xs);
    }
}

const id = new DList(x => x);

const cons = x => new DList(ys => [x].concat(ys));

const concat = xs => new DList(ys => xs.concat(ys));

const identityLeft  = id . concat([1, 2, 3]);
const identityRight = concat([1, 2, 3]) . id;

const associativityLeft  = concat([1, 2]) . cons(3);
const associativityRight = cons(1) . concat([2, 3]);

console.log(identityLeft.apply([]));  // [1,2,3]
console.log(identityRight.apply([])); // [1,2,3]

console.log(associativityLeft.apply([]));  // [1,2,3]
console.log(associativityRight.apply([])); // [1,2,3]

与常规列表(函数列表,而不是 JavaScript 数组)相比,使用差异列表的优点是串联更高效,因为列表是从右到左串联的。因此,如果您连接多个列表,它不会一遍又一遍地复制相同的值。


教堂编码列表 https://en.wikipedia.org/wiki/Church_encoding#Represent_the_list_using_right_fold

作为使用 Church 对进行编码的替代方案,可以通过使用其右折叠函数来识别列表来对其进行编码。例如,三个元素 x、y 和 z 的列表可以通过高阶函数进行编码,当应用于组合器 c 和值 n 时,返回 c x (c y (c z n))。

https://stackoverflow.com/a/51420884/6440264 https://stackoverflow.com/a/51420884/6440264

用户633183的解决方案 https://stackoverflow.com/a/51460654/783743太棒了。它使用使用右折叠对列表进行 Church 编码 https://en.wikipedia.org/wiki/Church_encoding#Represent_the_list_using_right_fold减少对延续的需要,从而使代码更简单,易于理解。这是她的解决方案,修改为foldr看似foldl:

const L = g => function (x, a) {
    switch (arguments.length) {
    case 1: return L((f, a) => f(g(f, a), x));
    case 2: return g(x, a);
    }
};

const A = L((f, a) => a);

const xs = A(1)(2)(3)(4)(5);

console.log(xs((x, y) => x + y, 0));        // 15
console.log(xs((x, y) => x * y, 1));        // 120
console.log(xs((a, x) => a.concat(x), [])); // [1,2,3,4,5]

Here g是迄今为止累积的教会编码列表。最初,它是空列表。呼唤g从右侧将其折叠。但是,我们也从右侧构建列表。因此,由于我们编写列表的方式,我们似乎正在构建列表并从左侧折叠它。


如果所有这些功能让您感到困惑,那么 user633183 真正做的是:

const L = g => function (x, a) {
    switch (arguments.length) {
    case 1: return L([x].concat(g));
    case 2: return g.reduceRight(x, a);
    }
};

const A = L([]);

const xs = A(1)(2)(3)(4)(5);

console.log(xs((x, y) => x + y, 0));        // 15
console.log(xs((x, y) => x * y, 1));        // 120
console.log(xs((a, x) => a.concat(x), [])); // [1,2,3,4,5]

正如您所看到的,她正在向后构建列表,然后使用reduceRight向后折叠向后列表。因此,看起来您正在构建并向前折叠列表。


我喜欢在不同列表中看到的是

  1. 看起来很自然、很容易理解。
  2. 通过连接(展平),它形成幺半群
  3. 恒等元是恒等函数,不需要提供外部初始值。

我不喜欢什么

  1. 至少,提供的示例代码依赖于 JavaScript Array

事实上,我在《教会名单》中喜欢/不喜欢的内容与上述相反。

I like

  1. 它独立于 JavaScript Array 实现,并且可以自行定义操作:用户633183的解决方案 https://stackoverflow.com/a/51460654/783743

我不喜欢

  1. 我不知道为什么一定不能向左折叠而必须向右折叠?

可以通过使用其右折叠函数来识别列表来对其进行编码

  1. 不清楚与幺半群的关系

  2. 特别是,Nil不是Identity元素(=恒等函数),并且示例代码需要提供外部初始值。

所以,我很好奇的是,是否有像 Church-list 这样的 Diffrence 列表的形式化。

规格是

  • 基本上,这是一个差异列表

  • 独立于 JavaScript 数组实现

  • 初始值是内置的恒等函数。

谢谢。


问题的根源

你这一系列问题的根源在于你坚持使用L(1)(2)(3)构造列表的语法。这种语法没有任何意义,人们一次又一次地告诉你不要使用这种语法:

  1. 用户633183的回答 https://stackoverflow.com/a/51286874/783743对于你的第一个问题:

    函数柯里化和可变参数并不能真正协同工作。一旦您意识到以下两个表达式不兼容,这个限制就会变得显而易见

    L (1)     -> [ 1 ]
    L (1) (2) -> [ 1, 2 ]
    

    Above L (1)返回一个列表,但在我们期望的第二个表达式中L (1)成为我们可以应用的函数2. L (1)可以是一个列表or它可以是一个生成列表的函数;它不能同时是两者。

  2. 贝尔吉的评论 https://stackoverflow.com/questions/51297054/how-to-store-data-of-a-functional-chain-of-monoidal-list#comment89595300_51297054关于你的第二个问题:

    首先,如果您想拥抱函数式编程,请避免可变参数函数或奇怪的混合返回类型。

  3. 用户633183的回答 https://stackoverflow.com/a/51460654/783743关于你的第三个问题:

    说到类型,让我们检查一下autoCons

    autoCons (1)                  // "lambda (x,n) => isFunction (x) ...
    autoCons (1) (2)              // "lambda (x,n) => isFunction (x) ...
    autoCons (1) (2) (3)          // "lambda (x,n) => isFunction (x) ...
    autoCons (1) (2) (3) (add, 0) // 6
    

    Well autoCons总是返回一个 lambda,但该 lambda 有一个我们无法确定的类型——有时它返回另一个相同类型的 lambda,有时它返回一个完全不同的结果;在这种情况下一些数字,6

    正因为如此,我们不能轻易混合和组合autoCons表达式与我们程序的其他部分。如果你放弃这个不正当的驱动来创建可变参数柯里化接口,你可以制作一个autoCons这是可输入的

我没有看到任何充分的理由使用L(1)(2)(3)语法,当你可以简单地写toList([1,2,3]):

// null :: List a
// cons :: (a, List a) -> List a
const cons = (head, tail) => ({ head, tail });

// xs :: List Number
const xs = cons(1, cons(2, cons(3, null))); // You can either construct a list manually,

// toList :: Array a -> List a
const toList = array => array.length ? cons(array[0], toList(array.slice(1))) : null;

// ys :: List Number
const ys = toList([1,2,3]); // or you can construct a list from an array.

console.log(xs);
console.log(ys);

此外,如果您使用的唯一原因是L(1)(2)(3)语法是“有效地”将元素推到列表末尾,那么您也可以对普通列表执行此操作。只需向后构建列表并使用cons将新元素放在列表的开头。

列表的代数结构

您似乎对列表的结构有一些非正统的信念:

  1. First, 你相信 https://stackoverflow.com/questions/51418212/extracting-data-from-a-function-chain-without-arrays/51460654#comment89918805_51460654列表的头部应该始终为零:

    lisp/Scheme 教科书中解释的构建列表的传统方法是非常错误的。 Nil 不应该位于列表的尾部,而应该位于列表的头部。 Lisp/Scheme 扭曲的列表结构(尾部 0 =nil)给编程世界带来了很多混乱。

  2. Second, 你相信 https://stackoverflow.com/users/6440264/bayesian-study您不必为列表折叠提供初始值:

    我仍然不知道您坚持使用“init”值进行折叠等的任何理由,查看一些库,他们不使用“init”,我认为它们更合理。github.com/benji6/church/blob/master/src/lists.js https://github.com/benji6/church/blob/master/src/lists.js准确地说,他们基本上使用 Zero=Identity 进行 init,这样更有意义。

这两种信念都是无知的。要理解为什么我们需要查看列表的代数结构:

   ┌──────────────────────────── A List of a
   │   ┌──────────────────────── is
   |   |   ┌──────────────────── either null
   |   |   |  ┌───────────────── or
   |   |   |  |   ┌───────────── cons of
   |   |   |  |   |   ┌───────── an a and
   │   |   |  |   |   |     ┌─── another List of a.
┌──┴─┐ │ ┌─┴┐ | ┌─┴┐  |  ┌──┴─┐
List a = null | cons (a, List a)

列表可以是空的,也可以是非空的。空列表表示为null。非空列表是通过使用以下方法将新元素放在另一个(可能是空)元素列表前面而形成的cons。我们将新元素放在原始列表的前面而不是后面,因为这样更自然:

cons(1, cons(2, cons(3, null))); // This is easier to read and write.

snoc(snoc(snoc(null, 1), 2), 3); // This is more difficult to read and write.

Note:使用本质上没有什么问题snoc。我们可以定义List as List a = null | snoc (List a, a)。然而,使用起来更自然cons。现在,取决于我们是否使用cons or snoc定义List数据类型,将新元素放在列表前面或将新元素放在列表后面都会变得昂贵:

       in front of     behind
     ┌─────────────┬─────────────┐
cons │ Inexpensive │  Expensive  │
     ├─────────────┼─────────────┤
snoc │  Expensive  │ Inexpensive │
     └─────────────┴─────────────┘

Note:接下来的两段使用 Haskell 语法。

差异列表 https://en.wikipedia.org/wiki/Difference_list用于通过将列表的串联延迟到需要时然后以最有效的顺序串联它们来分摊昂贵操作的成本。例如,假设我们有表达式as ++ bs ++ cs ++ ds我们正在连接四个列表。如果我们使用cons实施List那么最有效的串联顺序是as ++ (bs ++ (cs ++ ds)),这就是为什么(++) http://hackage.haskell.org/package/base-4.11.1.0/docs/Prelude.html#v:-43--43-Haskell 中的运算符是右结合的。另一方面,如果我们使用snoc实施List那么最有效的串联顺序是((as ++ bs) ++ cs) ++ ds.

当使用cons实施List,差异列表的形式为(xs ++) where xs是一个常规列表。我们可以使用常规函数组合将它们向前组合(即(as ++) . (bs ++) . (cs ++) . (ds ++))。当使用snoc实施List,差异列表的形式为(++ xs) where xs是一个常规列表。我们可以使用常规函数组合向后组合它们(即(++ ds) . (++ cs) . (++ bs) . (++ as))。这是使用的另一个原因cons实施List是更优选的。

现在,让我们换个话题来讨论非空列表的各个部分。当涉及到列表时(无论我们是否使用cons实施List or the snoc实施List), 条款head, tail, init and last有非常具体的含义:

   head          tail
     │  ┌──────────┴─────────┐
cons(1, cons(2, cons(3, null)));
└──────┬─────┘       │
     init          last

              init         last
     ┌──────────┴─────────┐  │
snoc(snoc(snoc(null, 1), 2), 3);
                     │   └─┬─┘
                   head  tail
  1. The head http://hackage.haskell.org/package/base-4.11.1.0/docs/Prelude.html#v:head非空列表的第一个元素是列表的第一个元素。
  2. The tail http://hackage.haskell.org/package/base-4.11.1.0/docs/Prelude.html#v:tail非空列表的 是除了列表的第一个元素之外的所有内容。
  3. The init http://hackage.haskell.org/package/base-4.11.1.0/docs/Prelude.html#v:init非空列表的内容是列表中除最后一个元素之外的所有内容。
  4. The last http://hackage.haskell.org/package/base-4.11.1.0/docs/Prelude.html#v:last非空列表的 是列表的最后一个元素。

因此,取决于我们是否使用cons or snoc定义List数据类型,要么head and tail or init and last变得昂贵:

       head / tail   init / last
     ┌─────────────┬─────────────┐
cons │ Inexpensive │  Expensive  │
     ├─────────────┼─────────────┤
snoc │  Expensive  │ Inexpensive │
     └─────────────┴─────────────┘

不管怎样,这就是为什么“Nil 不应该在列表的尾部,而应该在头部”这样的说法毫无意义的原因。列表的头是列表的第一个元素。 Nil 不是列表的第一个元素。因此,声明 nil 应该始终是列表的头部是不合逻辑的。


现在,让我们转向折叠。取决于我们是否使用cons or snoc定义List数据类型,要么foldl or foldr变成尾递归:

               foldl                  foldr
     ┌──────────────────────┬──────────────────────┐
cons │    Tail Recursion    │ Structural Recursion │
     ├──────────────────────┼──────────────────────┤
snoc │ Structural Recursion │    Tail Recursion    │
     └──────────────────────┴──────────────────────┘

如果语言执行,尾递归通常会更有效尾调用优化 https://stackoverflow.com/questions/310974/what-is-tail-call-optimization。然而,结构递归更natural https://stackoverflow.com/q/32260444/783743,并且在具有惰性求值的语言中变得更有效率 https://stackoverflow.com/q/3429634/783743它可以处理无限的数据结构。说到无限数据结构,cons实施无限向前增长(即cons(1, cons(2, cons(3, ....))))而snoc实现无限向后增长(即snoc(snoc(snoc(...., 1), 2), 3))。另一个选择的理由cons over snoc.

无论如何,让我们尝试理解为什么需要折叠的初始值。假设我们有以下列表,xs = cons(1, cons(2, cons(3, null)))我们用它来折叠它foldr:

  cons                                         func
 /    \                                       /    \
1    cons                                    1    func
    /    \      -> foldr(func, init, xs) ->      /    \
   2    cons                                    2    func
       /    \                                       /    \
      3    null                                    3    init

正如你所看到的,当我们使用foldr我们基本上正在替换每一个cons with func我们正在替换null with init。这允许您通过折叠第一个列表来执行诸如附加两个列表之类的操作,替换cons with cons and null与第二个列表,ys = cons(4, cons(5, cons(6, null))):

  cons                                       cons
 /    \                                     /    \
1    cons                                  1    cons
    /    \      -> foldr(cons, ys, xs) ->      /    \
   2    cons                                  2    cons
       /    \                                     /    \
      3    null                                  3    cons
                                                     /    \
                                                    4    cons
                                                        /    \
                                                       5    cons
                                                           /    \
                                                          6    null

现在,如果您不提供初始值,那么您就不会保留列表的结构。因此,您将无法附加两个列表。事实上,您甚至无法重建相同的列表。考虑:

  cons                                    func
 /    \                                  /    \
1    cons                               1    func
    /    \      -> foldr1(func, xs) ->      /    \
   2    cons                               2    func
       /    \                                  /
      3    null                               3

Using foldr1您可以在不提供初始值的情况下找到列表的总和(即foldr1(plus, xs)),但是如何在不诉诸于的情况下重建相同的列表巫术 https://stackoverflow.com/a/51433691/783743?如果您愿意提供初始值,那么您可以优雅地编写foldr(cons, null, xs)。否则的话,是不可能做到的,除非你打破函数式编程的原则,利用副作用从内部人为地提供一个初始值func本身。无论哪种方式,您都将提供一个初始值,无论是通过显式指定初始值还是通过将列表的最后一个元素作为特殊情况处理func.

选择更简单的替代方案。明确提供初始值。作为Python之禅 https://www.python.org/dev/peps/pep-0020/ states:

美丽总比丑陋好。
显式的比隐式的好。
简单总比复杂好。
...
特殊情况还不足以违反规则。

无论如何,继续最后一部分。

您正在寻找的答案(以及更多)

我不回答你的任何问题就给你讲课是不合适的。因此:

  1. 关于差异列表,您的以下说法是错误的:

    1. 单位元是单位函数,并且无需提供外部初始值。

    实际上,如果您折叠差异列表,那么您仍然需要提供初始值。作为参考,请参阅foldr https://hackage.haskell.org/package/dlist-0.8.0.4/docs/Data-DList.html#v:foldr函数从Data.DListHackage 上的包。

  2. 关于 Church 编码列表,您有以下问题:

    1. 我不知道为什么一定不能向左折叠而必须向右折叠?

    因为你的古怪L(1)(2)(3)语法,您只能向后构建列表(即L(1)(2)(3) = cons(3, cons(2, cons(1, null))))。因此,如果你想“向前”折叠列表,那么你必须使用foldr代替foldl。请注意,如果我们使用snoc代替cons那么它实际上是向前的(即L(1)(2)(3) = snoc(snoc(snoc(null, 1), 2), 3))。这是从以下事实得出的:snoc只是cons论点翻转了。所以,foldr for cons相当于foldl for snoc反之亦然,这是 user633183 注意到的。

    请注意,我最初使用延续的解决方案实际上使用了foldl for cons,但为了做到这一点,我必须以某种方式反转列表,因为它是向后构建的。这就是延续的目的,颠倒列表。后来我才意识到我根本不需要颠倒列表。我可以简单地使用foldr代替foldl.

  3. 关于你关于教会编码列表的第二点:

    1. 不清楚与幺半群的关系

    所有列表都是幺半群,其中单位元素是null二元运算是append = (xs, ys) => foldr(cons, ys, xs)。注意foldr(cons, null, xs) = xs(左身份)和foldr(cons, ys, null) = ys(正确的身份)。此外,foldr(cons, zs, foldr(cons, ys, xs))相当于foldr(cons, foldr(cons, zs, ys), xs)(关联性)。

  4. 关于你关于教会编码列表的第三点:

    1. 特别是,Nil不是Identity元素(=恒等函数),并且示例代码需要提供外部初始值。

    是的,nil 实际上是列表的单位元素。如果List数据类型被实现为差异列表,然后 nil 是恒等函数。否则,那就是另外一回事了。尽管如此,nil 始终是列表的标识元素。

    我们已经讨论了为什么外部初始值是必要的。如果您不提供它们,那么您将无法执行某些操作,例如append. You have提供附加两个列表的初始值。要么显式提供初始值,要么通过处理第一个元素(当使用foldl)或最后一个元素(当使用foldr)作为一个特例(从而打破了函数式编程的原则)。

  5. 最后,关于您梦想的界面:

    所以,我很好奇的是,是否有像 Church-list 这样的 Diffrence 列表的形式化。

    你为什么想这么做?您希望实现什么目标?教会编码仅在理论上有意义。在实践中效率不是很高。此外,差异列表仅在您随意连接列表时才有用(从而利用差异列表的幺半群结构来展平它们)。将两者结合起来是一个非常糟糕的主意。

无论如何,我希望你停止问这样的问题并花一些时间阅读SICP https://mitpress.mit.edu/sites/default/files/sicp/index.html.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用右折叠和差异列表对列表进行 Church 编码 的相关文章

随机推荐

  • 关闭父级而不关闭子级

    我有一个项目 其中弹出一个设置对话框 父级 当用户点击 继续 时 将打开一个主对话框 子级 在主对话框中 用户可以重新编辑设置对话框 父级 当用户单击 X 关闭设置对话框时 应用程序终止 我认为这是因为我们关闭了父级并处理了它的所有子级 是
  • 重新定义 Mathematica 中的非交换乘法

    Mathematicas NonCommutativeMultiply 不会简化诸如 a 0 0 a 0 a 1 1 a a or a a a 2 我想重新定义 去做这个 我使用 NCAlgebra 来执行此操作 但我需要 ReplaceR
  • PHP 生成一个预先定义长度的随机数

    我正在尝试使用 mt rand 创建一个函数来生成真正的随机数 因为 rand 还不够 问题是我需要预先定义数字的长度 假设我需要一个 10 位随机数 无论如何 我一直在搞乱 这就是我想出的 function randomNumber le
  • 如何将 Scalaz 的 traverse 和 traverseU 与 Either 结合使用

    是否可以使用 Scalaz traverse and traverseU with Either代替Option 对于以下代码 val list List 1 2 3 def f i Int Either Int String if i g
  • Ajax根据行获取表值

    我可以从行中获取第一个和最后一个值 但无法获取行中的第二个和第三个值 谁能帮我 这是我的代码 gt 网页 tr td one td td two td td three td td four td td td tr
  • java.lang.IllegalArgumentException:解析错误 - 日期格式错误?

    我使用变量 CURRENT DATE 将当前日期存储在 SQLite 数据库中 我发现使用的日期格式是yyyy mm dd在相同的 我想解析代码中的日期 但收到此错误 java lang IllegalArgumentException 解
  • C# 将字符转换为字节(十六进制表示)

    这似乎是一个简单的问题 但我无法弄清楚 我需要转换这个字符 lt 以字节 十六进制表示 表示 但如果我使用 byte b Convert ToByte lt i get 60 十进制表示 代替3c 60 0x3C 你已经有了正确的答案 但你
  • 如何合并两个ArrayList而不重复? [复制]

    这个问题在这里已经有答案了 我有两个数组列表 ArrayList one A B C D E ArrayList two B D F G 我想要我的最终 ArrayList 它将有All一的元素和只存在于二而不存在于一的元素 所以 Arra
  • 如何在 SQL 中比较两个表并删除重复行?

    我有两个表 如果第二个表中存在行的精确副本 则需要从第一个表中删除行 有谁有我如何在 MSSQL 服务器中执行此操作的示例吗 好吧 在某些时候你将不得不检查所有列 不妨加入 DELETE a FROM a first table INNER
  • StreamReader.ReadToEnd() 使用什么字符编码?

    使用什么字符编码StreamReader ReadToEnd 使用下面的 b 而不是 a 的原因是什么 如果使用 a 是否存在字符编码问题的风险 而不是 b 还有其他方法比 a 和 b 更好吗 a Dim strWebResponse As
  • 为 Android 应用游戏制作重启按钮

    我正在开发一个 Android 应用程序 当我运行主要活动时 该应用程序会按我想要的方式运行 但在你在游戏中死亡后会停止 我想让你在游戏中死亡后出现一个重新启动按钮 到目前为止 我已经制作了一个带有按钮和主要活动背景的新布局 我在源文件夹中
  • 如何仅在 Option 为 None 时有条件地执行代码?

    如果函数返回可选值 我不想采取行动 我如何测试None仅案例 这段代码可以工作 但看起来很糟糕 let v ffunc match v None gt callproc Some x gt 在C中 我可以写 int x ffunc if x
  • 将 less 字符串传递给 less.js 并接收 css?

    less js中有一个函数可以传递一串less代码并返回css代码吗 我希望创建一个实时的 less 编辑环境 这样用户可以编辑 less 字符串 我可以重新编译它并显示 css 我看到有一个php解决方案 http leafo net l
  • 如何检查PCNTL模块是否存在

    我编写简单的需求检查脚本 它检查安装的所有必需的 PHP 模块 我需要检查 pcntl 是否已安装 但该模块只能在 cgi 环境中访问 对于 Web 查询不可见 extension loaded pcntl 和 function exist
  • 在 Grails 中获取特定数据源的 SessionFactory

    因此 如果我想在支持多个数据源之前使用 Grails 正在使用的会话进行直接 SQL 查询 我可以这样做 def conn new Sql sessionFactory currentSession connection 现在的问题是我有多
  • iOS:如何在排序的 NSMutableArray 中查找插入位置

    我有一个排序对象的 NSMutableArray 它们显示在 UITableView 中 我想将一个新对象插入数组并更新表视图 这需要新插入对象的索引 我找不到任何系统消息来告诉我在数组中正确的插入索引 我需要更新表视图 我能找到的最好的是
  • Java 使用 OpenGL Stencil 创建 Outline

    我正在尝试在其顶部渲染对象的轮廓 但遇到了一些困难 我不太擅长 OpenGL 所以大部分内容都来自以下教程 想要的效果应该是这样的 但目前的结果是这样的 我用来执行此操作的代码是 GL11 glPushAttrib GL11 GL ALL
  • SwiftUI - 带有条件闭包的 if let 的替代方案

    我正在尝试在 SwiftUI 中实现以下内容 struct PersonView View State private var age Int 0 var body some View VStack Text Just a test if
  • 使用 Tkinter,有没有一种方法可以在不明显缩放窗口的情况下获得可用的屏幕尺寸?

    您可以获得可用的屏幕尺寸 屏幕减去任务栏 无论它位于何处 如下所示 import Tkinter as tk root tk Tk root state zoomed root update usable width root winfo
  • 使用右折叠和差异列表对列表进行 Church 编码

    这是之后的连续问题 如何存储Monoidal List函数链的数据 https stackoverflow com questions 51297054 how to store data of a functional chain of