LINUX.ORG.RU

Нестрогие вычисления в императивных ЯП


1

1

Evaluation strategy

Возможна ли такая оптимизация компилятором (или VM), когда вычисление передаваемых параметров происходит при необходимости? К примеру:

void Func(int a, int b) {
   if ( a > 0 ) { // 95% true
     CallA(); return;
   }
   if ( a > -1 && b > 0 ) {
     CallB();
   }
}
...
Func( GetValueFromDisk(), GetValueFromNetwork() );

Если да, то может ли императивный ЯП (c,java) иметь нестрогую модель вычислений?

Если присмотреться, то в коде большинства программ довольно много параметров методов, значения которых не используется в ряде случаев.

А скала считается за императивный язык?

scala> def g(x: => Int) = { println("g"); x }
g: (x: => Int)Int

scala> def f(x: Int) = { println("f"); x }
f: (x: Int)Int

scala> f(100/0)
java.lang.ArithmeticException: / by zero
  ... 32 elided

scala> g(100/0)
g
java.lang.ArithmeticException: / by zero
  at $anonfun$1.apply$mcI$sp(<console>:9)
  at .g(<console>:7)
  ... 32 elided
anonymous
()

императивный ЯП - да.

c,java - врядли

Пример (picolisp):

(de func AB
     (let A (eval (car AB))
        (cond
           ((gt0 A) (prinl "CallA"))
           ((and (< -1 A) (gt0 (eval (cadr AB)))) (prinl "CallB")))))
Тест:
: (trace '+)
-> +
: (func (+ 2 0) (+ 1 0))
 + : 2 0
 + = 2
CallA
-> "CallA"
: (func (+ 0 0) (+ 1 0))
 + : 0 0
 + = 0
 + : 1 0
 + = 1
CallB
-> "CallB"

anonymous
()
Ответ на: комментарий от anonymous

А где это было написано, кстати, что в лямбду нужно просто заворачивать в таких случаях? Напомните книжку.

anonymous
()
Ответ на: комментарий от anonymous
import java.math.BigInteger;
import java.util.function.IntSupplier;

public class Main {
	public static void f(final IntSupplier a, final IntSupplier b) {
		if (a.getAsInt() > 0) {
			System.out.println("95%");
		} else if (a.getAsInt() > -1 && b.getAsInt() > 0) {
			System.out.println("5%");
		}
	}

	public static void main(String[] args) {
		f(() -> new BigInteger("1").intValue(),
		  () -> new BigInteger("10000000000000000000000000002317846192736914")
				.modInverse(new BigInteger("101231241243245453r257634756464573547")).intValue());

	}
}

anonymous
()
Ответ на: комментарий от nerdogeek

Не, это просто специализация для примитивных типов (чтоб не Supplier<Integer>, боксинг-анбоксинг, то-се). В общем случае там просто дженерик Supplier<T>, и переход от передачи объекта к передаче завернутого объекта - просто заменить f(x) на f(() -> x). Ящитаю, для джявы это более чем достойно. В смысле, это же легаси-язык, это по ее меркам не такой уж и костыль)

anonymous
()

Inlining + code motion сделают свое грязное дело в этом случае. А вот между вызовами вряд ли, без агрессивной специализации не выйдет.

anonymous
()
Ответ на: комментарий от anonymous

А где это было написано, кстати, что в лямбду нужно просто заворачивать в таких случаях? Напомните книжку.

Не SICP ли?

korvin_ ★★★★★
()

Сишка не может, потому что эффекты аргументов гарантируются до вызова by design. Про яву не знаю. Если аргумент тяжелый, но потенциально неиспользуемый, то его заворачивают в аксессор, в твоем случае int (*GetValue)(enum ValueSource), так что пример высосан из пальца, как и начальное утверждение — обычно человек понимает, что будет использовано, и передает 0/NULL/etc в нужные места, а интерфейсы строятся подобным же образом, не требуя аргументов по рандому. Способ вычислений и ленивые вычисления в частности это часть философии программирования, и уж точно не метод оптимизации или панацея.

arturpub ★★
()
Ответ на: комментарий от anonymous

Вопрос стоял о возможностях имеративных ЯП.

Если хочешь блеснуть знаниями, то никто не запрещает предъявить реализацию на твоем любимом компилируемом ЯП.

Если не можешь - иди сам по указанному тобой адресу.

anonymous
()
Ответ на: комментарий от theNamelessOne

Да. Еще бывают ссылки на методы, они иногда короче пишутся, но правила слегка идиотские. Я где-то правильную мысль читал (про лямбды, стримы и остальное из 8ки), что они с правильными намерениями сделали слегка монструзное поделие. Просто изначально в модель это заложено не было, вот и выглядит странно. Хотя и так лучше, чем как раньше.

anonymous
()

Возможна ли такая оптимизация компилятором (или VM), когда вычисление передаваемых параметров происходит при необходимости? К примеру:

Естественно.

может ли императивный ЯП (c,java) иметь нестрогую модель вычислений?

Гипотетический императивный - да, Си и Java - нет.

tailgunner ★★★★★
()
Ответ на: комментарий от arturpub

Ну для сишки да, вызов ф-ии - это sequence point. Но если просто помечтать об агрессивных (безопасных?) оптимизациях.

nerdogeek
() автор топика
Ответ на: комментарий от theNamelessOne

Еще можно каррировать руками :)

public static <Arg1, Arg2, Ret> Function<Arg1, Function<Arg2, Ret>> curryBy1stArg(final BiFunction<Arg1, Arg2, Ret> biFunction) {
	return arg -> x -> biFunction.apply(arg, x);
}

public static void main(String[] args) {
	BiFunction<Date, String, String> wtf = (date, str) -> str + " " + date;
	System.out.println(wtf.apply(Date.from(Instant.now()), "current time is"));
	Function<Date, Function<String, String>> currentTime = curryBy1stArg(wtf);
	Function<String, String> decorateCurrentTime = currentTime.apply(Date.from(Instant.now()));
	System.out.println(decorateCurrentTime.apply("VREMYA PO MOSKWE"));
	System.out.println(decorateCurrentTime.apply("time in urupinsk"));
}
anonymous
()
Ответ на: комментарий от nerdogeek

Зачем они нужны, если ты мыслями и методологией разработки остаешься в сишке? Большинство тех борщевиков (я не про тебя разумеется), кто кода еще не писал, но уже открыл для себя фп и хаскел, считают, что на императивщине сидят идиоты, не знакомые с моделями вычислений и с эффективными методиками их применения для решения задач. Оставим их в болоте их чсв и прикинем: сколько реально ленивых оптимизаций можно применить к среднестатистической императивной программе, чтобы получить ускорение за пределами погрешности?

Изменение же мышления и принципов для *будущих* программ изменит язык настолько, что проще сейчас взять готовый с таким свойством и переписать для него стдлиб с именами из сишки. Реализуй на хаскеле strcmp, fopen, sprintf и прочие — скипнешь нехилый участок ненужного пути.

arturpub ★★
()
Ответ на: комментарий от theNamelessOne

Он есть в пределах списков параметров лямбд (ну то есть, он там такой локальный, что локальнее некуда). Очень не хватает, да. Вывода типов или хотя бы алиасов на эти страшнючие дженерики.

anonymous
()

Можно даже самому такое сбоку приделать чтобы, например, твои GetValueFromDisk и GetValueFromNetwork возвращали нечто ленивое что будет вычеслено в момент использования (что-то типа питоновских генераторов).

Но тут нужны чёткие соглашения как это всё работает и как надо писать код. GetValueFromNetwork не является чистой и что если для корректной работы программы нужны энергичные вычисления? Один из вариантов — переизобрести хаскель.

true_admin ★★★★★
()

Без контроля над сайд-эффектами это будеть ОЧЕНЬ криво работать. А с ним есть, по сути, один более-менее развитый язык, угадай название.

Miguel ★★★★★
()
Ответ на: комментарий от nerdogeek

В хаскеле тоже обёртки, если присмотреться, просто они везде (всякий примитивный * — обёртка над примитивным #, нестрогий data — сам себе обёртка). А так можно лямбды и прочие functor-like / интерфейсные объекты кидать.

quasimoto ★★★★
()
Последнее исправление: quasimoto (всего исправлений: 1)
Ответ на: комментарий от Begemoth

the world's finest imperative programming language

Я бы сакзал

the world's ugliest imperative programming language

phill
()
Ответ на: комментарий от quasimoto
#include <cstdio>
#include <ftl/lazy.h>

void f(ftl::lazy<int> a, ftl::lazy<int> b) {
    if (*a > 0) printf("a = %d\n", *a);
    else if (*a > -1 && *b > 0) printf("a = %d, b = %d\n", *a, *b);
}

int main() {
    {
        auto a = ftl::defer([]() { puts("compute a"); return 1; });
        auto b = ftl::defer([]() { puts("compute b"); return 2; });
        f(a, b);
    }
    {
        auto a = ftl::defer([]() { puts("compute a"); return 0; });
        auto b = ftl::defer([]() { puts("compute b"); return 1; });
        f(a, b);
    }
}

/*
compute a
a = 1
compute a
compute b
a = 0, b = 1
 */
quasimoto ★★★★
()
Последнее исправление: quasimoto (всего исправлений: 1)

А какое вообще имеет отношение лень к ФП или императивной парадигме? На самомто деле, в хаскеле кастрированная лень. Настоящая лень, это call-by-name, когда кроме лени ты можешь получить аргумент по имени, а не только по значению.

phill
()
Ответ на: комментарий от phill

А какое вообще имеет отношение лень к ФП или императивной парадигме?

Ну да, фп это ссылочная прозрачность для компилятора, не более.

anonymous
()

Те быдлоязычки, которые я знаю, так не умеют. Но в сишечке можно напейсать говномакрос, в который обернуть функцию.

amomymous ★★★
()
Ответ на: комментарий от anonymous

Я давно сишку в руки не брал, но что-нибудь вроде

#define f_(a,b) int a1=a; if(a1>0) f(a1,0); else f(a1,b);

И тогда вызов f_(fast_func(), slow_func()) развернется в

int a1=fast_func(); if (a1>0) f(a1,0); else f(a1,slow_func());

Я сам вижу недостатки в этом примере, впрочем. Не уверен, можно ли избавиться ото всех.

amomymous ★★★
()

Быдлоязычковый пример

#! /usr/bin/python2.7
# coding: utf-8
"""
Реализация вычисления передаваемых в функцию параметров при необходимости.
"""

# Бяка №1, многословная
def _(*a):
    args = {}
    def wrapper(f):
        def inner():
            while 1:
                if f not in args:
                    args[f] = f()
                yield args[f]
        return inner
    return (wrapper(f)().next for f in a)

# Бяка №2, хацкерская
__ = lambda *args: ((lambda f: lambda x=None, a=[]: a and a[0] or a.append(f()) or a[0])(a) for a in args)

if __name__ == '__main__':
    def foo(a, b):
        if a():
            print a()
        if b():
            print b()
        if a() and b():
            print a(), b()

    def bar(n=[0]):
        n[0] += 1
        return 'bar' + str(n[0])

    def bazz(n=[0]):
        n[0] += 1
        return 'bazz' + str(n[0])

    foo(*_(bar, bazz))
    foo(*__(bar, bazz))
Virtuos86 ★★★★★
()
Ответ на: комментарий от tailgunner

Си и Java - нет.

ксати, gcc, в случае, если ф-ция объявлена инлайн, не должен это дело соптимизировать? по крайней мере, в показанном примере

next_time ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.