ما هي بدائل إدارة الموارد التلقائية موجودة ل Scala؟

StackOverflow https://stackoverflow.com/questions/2207425

  •  18-09-2019
  •  | 
  •  

سؤال

لقد رأيت العديد من الأمثلة على الذراع (إدارة الموارد التلقائية) على الويب ل Scala. يبدو أن طقوس ممر لكتابة واحدة، على الرغم من أن معظمها تبدو كبيرة مثل بعضها البعض. أنا فعل رؤية مثال رائع جدا باستخدام استمرار، رغم ذلك.

على أي حال، هناك الكثير من هذا الرمز له عيوب من نوع واحد أو آخر، لذلك احسب أنه سيكون من الجيد أن يكون لديك مرجع هنا على تجاوز المكدس، حيث يمكننا التصويت أكثر الأصوات والمناسبة.

هل كانت مفيدة؟

المحلول

الى الان SCALA 2.13. وقد دعمت أخيرا: try with resources باستخدام استخدام :)، مثال:

val lines: Try[Seq[String]] =
  Using(new BufferedReader(new FileReader("file.txt"))) { reader =>
    Iterator.unfold(())(_ => Option(reader.readLine()).map(_ -> ())).toList
  }

أو استخدام Using.resource تجنب Try

val lines: Seq[String] =
  Using.resource(new BufferedReader(new FileReader("file.txt"))) { reader =>
    Iterator.unfold(())(_ => Option(reader.readLine()).map(_ -> ())).toList
  }

يمكنك العثور على المزيد من الأمثلة من استخدام وثيقة.

فائدة لأداء إدارة الموارد التلقائية. يمكن استخدامه لإجراء عملية باستخدام الموارد، وبعد ذلك تطلق الموارد بترتيب عكسي لإنشائها.

نصائح أخرى

كريس هانسن بلوق الدخول "كتل الذراع" في Scala: إعادة النظر "من 3/26/09 يتحدث عن شريحة 21 من مارتن أوديسكي عرض فدمدم.. وبعد يتم أخذ هذه الكتلة التالية مباشرة من الشريحة 21 (بإذن):

def using[T <: { def close() }]
    (resource: T)
    (block: T => Unit) 
{
  try {
    block(resource)
  } finally {
    if (resource != null) resource.close()
  }
}

- اقتباس -

ثم يمكننا الاتصال بهذا الشكل:

using(new BufferedReader(new FileReader("file"))) { r =>
  var count = 0
  while (r.readLine != null) count += 1
  println(count)
}

ما هي عيوب هذا النهج؟ يبدو أن هذا النمط يعالج 95٪ من حيث أحتاج إلى إدارة الموارد التلقائية ...

يحرر: مقتطف رمز إضافي


Edit2: تمديد نمط التصميم - الاستلهم من بيثون with بيان ومعالجة:

  • البيانات لتشغيل قبل الكتلة
  • إعادة رمي استثناء اعتمادا على المورد المدار
  • التعامل مع اثنين من الموارد مع بيان واحد واحد باستخدام
  • التعامل مع الموارد الخاصة بالموارد من خلال توفير تحويل ضمني و Managed صف دراسي

هذا مع scala 2.8.

trait Managed[T] {
  def onEnter(): T
  def onExit(t:Throwable = null): Unit
  def attempt(block: => Unit): Unit = {
    try { block } finally {}
  }
}

def using[T <: Any](managed: Managed[T])(block: T => Unit) {
  val resource = managed.onEnter()
  var exception = false
  try { block(resource) } catch  {
    case t:Throwable => exception = true; managed.onExit(t)
  } finally {
    if (!exception) managed.onExit()
  }
}

def using[T <: Any, U <: Any]
    (managed1: Managed[T], managed2: Managed[U])
    (block: T => U => Unit) {
  using[T](managed1) { r =>
    using[U](managed2) { s => block(r)(s) }
  }
}

class ManagedOS(out:OutputStream) extends Managed[OutputStream] {
  def onEnter(): OutputStream = out
  def onExit(t:Throwable = null): Unit = {
    attempt(out.close())
    if (t != null) throw t
  }
}
class ManagedIS(in:InputStream) extends Managed[InputStream] {
  def onEnter(): InputStream = in
  def onExit(t:Throwable = null): Unit = {
    attempt(in.close())
    if (t != null) throw t
  }
}

implicit def os2managed(out:OutputStream): Managed[OutputStream] = {
  return new ManagedOS(out)
}
implicit def is2managed(in:InputStream): Managed[InputStream] = {
  return new ManagedIS(in)
}

def main(args:Array[String]): Unit = {
  using(new FileInputStream("foo.txt"), new FileOutputStream("bar.txt")) { 
    in => out =>
    Iterator continually { in.read() } takeWhile( _ != -1) foreach { 
      out.write(_) 
    }
  }
}

دانيال،

لقد نشرت مؤخرا مكتبة Scala-Arm لإدارة الموارد التلقائية. يمكنك العثور على الوثائق هنا: https://github.com/jsuereth/scala-arm/wiki.

تدعم هذه المكتبة ثلاثة أنماط الاستخدام (حاليا):

1) الحتمية / للتعبير:

import resource._
for(input <- managed(new FileInputStream("test.txt")) {
// Code that uses the input as a FileInputStream
}

2) نمط مناديك

import resource._
import java.io._
val lines = for { input <- managed(new FileInputStream("test.txt"))
                  val bufferedReader = new BufferedReader(new InputStreamReader(input)) 
                  line <- makeBufferedReaderLineIterator(bufferedReader)
                } yield line.trim()
lines foreach println

3) نمط مستمر محدد

إليك خادم TCP "ECHO":

import java.io._
import util.continuations._
import resource._
def each_line_from(r : BufferedReader) : String @suspendable =
  shift { k =>
    var line = r.readLine
    while(line != null) {
      k(line)
      line = r.readLine
    }
  }
reset {
  val server = managed(new ServerSocket(8007)) !
  while(true) {
    // This reset is not needed, however the  below denotes a "flow" of execution that can be deferred.
    // One can envision an asynchronous execuction model that would support the exact same semantics as below.
    reset {
      val connection = managed(server.accept) !
      val output = managed(connection.getOutputStream) !
      val input = managed(connection.getInputStream) !
      val writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(output)))
      val reader = new BufferedReader(new InputStreamReader(input))
      writer.println(each_line_from(reader))
      writer.flush()
    }
  }
}

يجعل الرمز استخدامات من نوع المورد، لذلك فمن الممكن التكيف مع معظم أنواع الموارد. لديها حرارة لاستخدام الكتابة الهيكلية ضد الفصول بأي طريقة إغلاق أو منتخب. يرجى مراجعة الوثائق واسمحوا لي أن أعرف إذا كنت تفكر في أي ميزات مفيدة للإضافة.

هنا جيمس اليري الحل باستخدام المستمرات:

// standard using block definition
def using[X <: {def close()}, A](resource : X)(f : X => A) = {
   try {
     f(resource)
   } finally {
     resource.close()
   }
}

// A DC version of 'using' 
def resource[X <: {def close()}, B](res : X) = shift(using[X, B](res))

// some sugar for reset
def withResources[A, C](x : => A @cps[A, C]) = reset{x}

فيما يلي الحلول مع وبدون استمرار المقارنة

def copyFileCPS = using(new BufferedReader(new FileReader("test.txt"))) {
  reader => {
   using(new BufferedWriter(new FileWriter("test_copy.txt"))) {
      writer => {
        var line = reader.readLine
        var count = 0
        while (line != null) {
          count += 1
          writer.write(line)
          writer.newLine
          line = reader.readLine
        }
        count
      }
    }
  }
}

def copyFileDC = withResources {
  val reader = resource[BufferedReader,Int](new BufferedReader(new FileReader("test.txt")))
  val writer = resource[BufferedWriter,Int](new BufferedWriter(new FileWriter("test_copy.txt")))
  var line = reader.readLine
  var count = 0
  while(line != null) {
    count += 1
    writer write line
    writer.newLine
    line = reader.readLine
  }
  count
}

وهنا اقتراح تحسين Tiark ROMPF:

trait ContextType[B]
def forceContextType[B]: ContextType[B] = null

// A DC version of 'using'
def resource[X <: {def close()}, B: ContextType](res : X): X @cps[B,B] = shift(using[X, B](res))

// some sugar for reset
def withResources[A](x : => A @cps[A, A]) = reset{x}

// and now use our new lib
def copyFileDC = withResources {
 implicit val _ = forceContextType[Int]
 val reader = resource(new BufferedReader(new FileReader("test.txt")))
 val writer = resource(new BufferedWriter(new FileWriter("test_copy.txt")))
 var line = reader.readLine
 var count = 0
 while(line != null) {
   count += 1
   writer write line
   writer.newLine
   line = reader.readLine
 }
 count
}

أرى تطور تدريجي 4 خطوة للقيام الذراع في Scala:

  1. لا الذراع: الأوساخ
  2. إغلاق فقط: أفضل، ولكن عدة كتل المتداخلة
  3. استمرار موناد: استخدم لتسوية التعشيش، ولكن الانفصال غير الطبيعي في كتلتين
  4. المستمرات ذات الطراز المباشر: Nirava، آها! هذا هو أيضا البديل الأكثر آمنة من النوع: سيكون مورد خارج كتلة WithResource خطأ.

هناك ذراع خفيفة الوزن (10 خطوط) الذراع المضمنة مع الملفات الأفضل. يرى: https://github.com/pathikrit/better-files#light-arm.

import better.files._
for {
  in <- inputStream.autoClosed
  out <- outputStream.autoClosed
} in.pipeTo(out)
// The input and output streams are auto-closed once out of scope

إليك كيفية تنفيذها إذا كنت لا تريد المكتبة بأكملها:

  type Closeable = {
    def close(): Unit
  }

  type ManagedResource[A <: Closeable] = Traversable[A]

  implicit class CloseableOps[A <: Closeable](resource: A) {        
    def autoClosed: ManagedResource[A] = new Traversable[A] {
      override def foreach[U](f: A => U) = try {
        f(resource)
      } finally {
        resource.close()
      }
    }
  }

ماذا عن استخدام فئات النوع

trait GenericDisposable[-T] {
   def dispose(v:T):Unit
}
...

def using[T,U](r:T)(block:T => U)(implicit disp:GenericDisposable[T]):U = try {
   block(r)
} finally { 
   Option(r).foreach { r => disp.dispose(r) } 
}

بديل آخر هو tryclose الكسول المتقلب. انها جيدة جدا مع اتصالات قاعدة البيانات:

val ds = new JdbcDataSource()
val output = for {
  conn  <- TryClose(ds.getConnection())
  ps    <- TryClose(conn.prepareStatement("select * from MyTable"))
  rs    <- TryClose.wrap(ps.executeQuery())
} yield wrap(extractResult(rs))

// Note that Nothing will actually be done until 'resolve' is called
output.resolve match {
    case Success(result) => // Do something
    case Failure(e) =>      // Handle Stuff
}

ومع التدفقات:

val output = for {
  outputStream      <- TryClose(new ByteArrayOutputStream())
  gzipOutputStream  <- TryClose(new GZIPOutputStream(outputStream))
  _                 <- TryClose.wrap(gzipOutputStream.write(content))
} yield wrap({gzipOutputStream.flush(); outputStream.toByteArray})

output.resolve.unwrap match {
  case Success(bytes) => // process result
  case Failure(e) => // handle exception
}

مزيد من المعلومات هنا: https://github.com/choppythelbandjackight.

فيما يلي إجابة @ Chengpohi، تعديلها حتى تعمل مع Scala 2.8+، بدلا من Scala فقط 2.13 (نعم، يعمل مع Scala 2.13 أيضا):

def unfold[A, S](start: S)(op: S => Option[(A, S)]): List[A] =
  Iterator
    .iterate(op(start))(_.flatMap{ case (_, s) => op(s) })
    .map(_.map(_._1))
    .takeWhile(_.isDefined)
    .flatten
    .toList

def using[A <: AutoCloseable, B](resource: A)
                                (block: A => B): B =
  try block(resource) finally resource.close()

val lines: Seq[String] =
  using(new BufferedReader(new FileReader("file.txt"))) { reader =>
    unfold(())(_ => Option(reader.readLine()).map(_ -> ())).toList
  }
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top