ermouth: (ang)
[personal profile] ermouth

Мне сегодня на Stackoverflow попался изумительной красоты вопрос про javascript. Я, как обычно, невнимательно его прочитал и ответил немного не в кассу, ну, меня быстро поправили.

Вопрос чисто собеседовательный, хотя я такую задачку по неопытности как-то раз решал в жизни – и крепко в своё время затрахалася.

Задача: нужно сконструировать такую функцию add, которая делает вот так

add(1) >> 1
add(1)(2) >> 3
add(2)(3)(4) >> 9

Цепочка вызовов может быть любой длины, в скобках только числа.

Сначала может показаться, что задачка нерешаема. Потом – что нам нужно локально расширить объект Number какой-то конструкцией, которая позволит вызывать Number как функцию. Это можно сделать – но получается очень громоздко. В самом деле надо идти с другой стороны – делать такую функцию, которая при попытке её с чем нибудь сложить кастится в число.

Любопытно, что если переделывать Number получается практически то-же самое, просто значительно длиннее и от самого намбера там ничего не остаётся.

Сделать функцию, которая при попытке её с чем-нибудь сложить “прикидывается” числом совсем просто.

Дело в том, что любая функция в js – объект. У неё есть метод toString, который и управляет кастингом. Собственно, он и вызывается неявно, когда вычисляемое выражение требует привдения к примитивному типу. Если toString наследуется от прототипа – он вернёт исходный текст функции. Но мы можем его подменить – и он станет возвращать число.

Хак целиком выглядит вот так:


var add = (function() {
    var factory = function(value) {
        var fn = function(num) { return factory(value + num) };
        fn.toString = function() {return value};
        return fn;
    };
    return factory(0);
})();

Офигенно, по-моему, с эстетической точки зрения. На каждом вызове функции возвращается новая функция, которая скрыто каррирована значением предыдущего шага и которая прикидывается числом, если её закастить в примитивный тип.

Такой очень хитрый итератор.

Использовать в жизни я бы такое, правда, не посоветовал – оно mind-blowing, медленное и хрупкое. Как и всё, что красивое и бесполезное )

Date: 2015-02-12 12:05 pm (UTC)
From: [identity profile] klonkaktusa.livejournal.com
Почему вы пишите функцию add вместо функции curry?
А если вам потом понадобится partial application функции subtract?

Date: 2015-02-12 08:31 pm (UTC)
From: [identity profile] ermouth.livejournal.com
Потому что а) это _не_ curry, б) задавший вопрос написал add.

Date: 2015-02-13 02:51 am (UTC)
From: [identity profile] klonkaktusa.livejournal.com
а) Возможность заменить add(1,2,3,...) на add(1)(2)(3)... это curry же. Понятно, что можно поспорить, но лень :)
в) Тред на хабре http://habrahabr.ru/post/226325/ (если не видели), но вопрос про add я встречал намного раньше, странно что на SO он появился только сейчас.

Date: 2015-02-13 04:07 am (UTC)
From: [identity profile] ermouth.livejournal.com
Да, f(1,2,3) >> fc(1)(2)(3) – это каррирование. Только ведь у нас нет никакого add(1,2,3) на входе.

Это примерно как если бы вы смотрели на на f=x^2 и говорили «О, это же интеграл» только из тех соображений, что существует f=x, чья первообразная x^2/2.

-------

На SO уже нашли клон вопроса этого от 2011. На Хабре – наверное, видел, я его часто читаю – но в памяти не отложилось. Там кста не совсем то-же самое – накрытие valueOf не оптимальный выбор, лучше toString подменять.

Profile

ermouth: (Default)
ermouth

November 2021

S M T W T F S
 123456
78910111213
14151617181920
21 222324252627
282930    

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated May. 20th, 2025 03:30 am
Powered by Dreamwidth Studios